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 /* 23 * Copyright 2002 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 <sys/types.h> 30 #include <sys/param.h> 31 #include <sys/sunddi.h> 32 #include <sys/note.h> 33 #include <sys/promif.h> 34 #include <sys/sbdp_error.h> 35 #include <sys/sbdp_priv.h> 36 37 /* 38 * The purpose if this model is to make it easy to inject error at all 39 * decision making points, such that all code paths can be tested, and 40 * states arrived are expected and recoverable. 41 * 42 * Passthru command "inject-error" will be used for injecting 43 * errors. A typical error injection command will look like the 44 * following: 45 * 46 * cfgadm -x passthru -o inject-error=func_name:entry_point:value N0.SB0 47 * 48 * where "func_name" is the name of the function where error will be 49 * injected, "entry_point" is a number in the function to identify which 50 * decision making point it is that we are injecting error, and "value" 51 * is what we want the check to return. The last field is ignored, 52 * so it can be any valid attachment point. 53 * 54 * For example, if we want to inject error at the 3rd entry in function 55 * sbdp_disconnect_cpu (we start counting at 0), we will issue the 56 * following command: 57 * 58 * cfgadm -x passthru -o inject-error=sbdp_disconnect_cpu:3:-1 N0.SB0 59 * 60 * To clear the error, change the value to 0, or whatever success 61 * corresponds to in the particular function. 62 * 63 * cfgadm -x passthru -o inject-error=sbdp_disconnect_cpu:3:0 N0.SB0 64 * 65 * Since this command is mainly for debugging, not all illegal options 66 * are rejected. Non-digit strings are accepted for entry point and 67 * value. They will be translated to 0. 68 * 69 * Another passthru command "reset-error" is used to clear all errors 70 * that have been injected. The only argument it needs is a valid 71 * attachment point as the last field. 72 * 73 * NOTE: Once implemented, the error injection points should remain 74 * relatively stable as QA will be using them for testing. 75 */ 76 77 78 /* 79 * Variable that controls if error injection should be done or not 80 */ 81 #ifdef DEBUG 82 uint_t sbdp_do_inject = 1; 83 84 /* 85 * Different error injection types that sbdp_ie_type can be 86 */ 87 #define SBDP_IE_RANDOM 0 /* Returns randomly 0 or -1 */ 88 #define SBDP_IE_FAILURE 1 /* Returns -1 */ 89 #define SBDP_IE_DEFINED 2 /* Returns value from sbdp_error_matrix */ 90 91 /* 92 * Variable that controls what type of error injection to do 93 */ 94 int sbdp_ie_type = SBDP_IE_DEFINED; 95 96 /* 97 * Basic return values from sbdp_inject_error 98 */ 99 #define SUCCESS 0 100 #define FAILURE -1 101 102 /* 103 * Maximum number of error injection entry points 104 */ 105 #define SBDP_IE_MAX_ENTRIES 4 106 107 typedef struct error_matrix { 108 const char *func_name; 109 uint_t num_entries; 110 int entries[SBDP_IE_MAX_ENTRIES]; 111 } error_matrix_t; 112 113 static error_matrix_t sbdp_error_matrix[] = { 114 { "sbdp_disconnect_cpu", 3, 0, 0, 0, 0 }, 115 { "sbdp_connect_cpu", 3, 0, 0, 0, 0 }, 116 { "sbdp_cpu_poweron", 2, 0, 0, 0, 0 }, 117 { "sbdp_cpu_poweroff", 4, 0, 0, 0, 0 }, 118 /* Termination entry, must exist */ 119 { NULL, 0, 0, 0, 0, 0 }, 120 }; 121 122 static int sbdp_func_lookup(const char *func_name); 123 124 extern int sbdp_strtoi(char *p, char **pos); 125 126 /* 127 * sbdp error injector. The argument should be of the following format: 128 * 129 * inject_error=func_str:entry_str:value_str 130 * 131 * Returns ESBD_INVAL if arg is not of the above format, 132 * or if any of the fields are invalid. 133 * 134 * Returns ESBD_NOERROR after setting the correct entry in the error 135 * matrix to the value passed in. 136 */ 137 int 138 sbdp_passthru_inject_error(sbdp_handle_t *hp, void *arg) 139 { 140 _NOTE(ARGUNUSED(hp)) 141 142 char *arg_str, *func_str, *entry_str, *value_str; 143 int index, value; 144 size_t len = strlen(arg) + 1; 145 uint_t entry; 146 int rv = ESBD_NOERROR; 147 static char *f = "sbdp_passthru_inject_error"; 148 149 arg_str = kmem_alloc(len, KM_SLEEP); 150 (void) strcpy(arg_str, arg); 151 152 /* 153 * Find '=' in the argument. Return ESBD_INVAL if '=' is 154 * not found. 155 */ 156 if ((func_str = strchr(arg_str, '=')) == NULL) { 157 rv = ESBD_INVAL; 158 goto out; 159 } 160 161 /* 162 * Now func_str points to '=' in arg_str. Increment the pointer 163 * so it points to the begining of the function string. 164 * Find the first ':' in the argument. Return ESBD_INVAL if 165 * not found. 166 */ 167 if ((entry_str = strchr(++func_str, ':')) == NULL) { 168 rv = ESBD_INVAL; 169 goto out; 170 } 171 172 /* 173 * Now entry_str points to the first ':' in arg_str. Set it 174 * to '\0' to NULL terminate func_str. Increment the 175 * pointer so it points to the begining of the entry string. 176 */ 177 *entry_str++ = '\0'; 178 179 /* 180 * Now entry_str points to the begining of the entry string. 181 * Find the next ':' in the argument. Return ESBD_INVAL if 182 * ':' is not found. 183 */ 184 if ((value_str = strchr(entry_str, ':')) == NULL) { 185 rv = ESBD_INVAL; 186 goto out; 187 } 188 189 /* 190 * Now value_str points to the second ':' in arg_str. Set it 191 * to '\0' to NULL terminate entry_str. Increment the 192 * pointer so it points to the begining of the value string. 193 * The rest of the arg_str is taken as the value string. 194 */ 195 *value_str++ = '\0'; 196 197 /* 198 * Find this function in the matrix. Return ESBD_INVAL if 199 * the function name is not found. 200 */ 201 if ((index = sbdp_func_lookup(func_str)) == -1) { 202 rv = ESBD_INVAL; 203 goto out; 204 } 205 206 /* 207 * To reduce the amount of code we have to write, we tolerate 208 * non-number input for entry point, and translate it to 0. 209 */ 210 entry = (uint_t)sbdp_strtoi(entry_str, NULL); 211 212 if (entry >= sbdp_error_matrix[index].num_entries) { 213 rv = ESBD_INVAL; 214 goto out; 215 } 216 217 /* 218 * No checking for value. Non-number string will be translated 219 * to 0. 220 */ 221 value = sbdp_strtoi(value_str, NULL); 222 223 SBDP_DBG_ERR("%s: index = %d, entry = %d, value = %d\n", 224 f, index, entry, value); 225 226 /* 227 * Set value at the right entry. 228 */ 229 sbdp_error_matrix[index].entries[entry] = value; 230 231 out: 232 kmem_free(arg_str, len); 233 return (rv); 234 } 235 236 /* 237 * Reset all entries to 0. 238 */ 239 int 240 sbdp_passthru_reset_error(sbdp_handle_t *hp, void *arg) 241 { 242 _NOTE(ARGUNUSED(hp)) 243 _NOTE(ARGUNUSED(arg)) 244 245 uint_t i, j; 246 247 for (i = 0; sbdp_error_matrix[i].func_name != NULL; i++) 248 for (j = 0; j < SBDP_IE_MAX_ENTRIES; j++) 249 sbdp_error_matrix[i].entries[j] = 0; 250 251 return (ESBD_NOERROR); 252 } 253 254 int 255 sbdp_inject_error(const char *func_name, uint_t entry) 256 { 257 extern volatile clock_t lbolt; 258 int index; 259 int value; 260 static char *f = "sbdp_inject_error"; 261 262 if (sbdp_do_inject == 0) 263 return (SUCCESS); 264 265 switch (sbdp_ie_type) { 266 267 case SBDP_IE_RANDOM: 268 /* 269 * Since we usually only need a binary type of return 270 * value, use lbolt to generate the psuedo random 271 * response. 272 */ 273 value = (-(int)(lbolt % 2)); 274 break; 275 276 case SBDP_IE_FAILURE: 277 value = FAILURE; 278 break; 279 280 case SBDP_IE_DEFINED: 281 /* 282 * Don't inject error if can't find the function. 283 */ 284 if ((index = sbdp_func_lookup(func_name)) == -1) { 285 value = SUCCESS; 286 break; 287 } 288 289 /* 290 * Don't inject error if can't find the entry. 291 */ 292 if (entry >= sbdp_error_matrix[index].num_entries) { 293 value = SUCCESS; 294 break; 295 } 296 297 value = sbdp_error_matrix[index].entries[entry]; 298 break; 299 300 default: 301 value = SUCCESS; 302 break; 303 } 304 305 if (value != SUCCESS) 306 SBDP_DBG_ERR("%s: function=%s entry=%d value=%d\n", 307 f, func_name, entry, value); 308 309 return (value); 310 } 311 312 static int 313 sbdp_func_lookup(const char *func_name) 314 { 315 int i; 316 const char *name; 317 318 /* 319 * Linear search for a match 320 */ 321 for (i = 0; (name = sbdp_error_matrix[i].func_name) != NULL; i++) { 322 if (strcmp(name, func_name) == 0) 323 return (i); 324 } 325 326 /* 327 * Function name not found in matrix 328 */ 329 return (-1); 330 } 331 332 #endif /* DEBUG */ 333