xref: /illumos-gate/usr/src/uts/sun4u/serengeti/io/sbdp_error.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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