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