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
sbdp_passthru_inject_error(sbdp_handle_t * hp,void * arg)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
sbdp_passthru_reset_error(sbdp_handle_t * hp,void * arg)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
sbdp_inject_error(const char * func_name,uint_t entry)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
sbdp_func_lookup(const char * func_name)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