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
80uint_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 */
92int 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
105typedef 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
111static 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
120static int sbdp_func_lookup(const char *func_name);
121
122extern 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 */
135int
136sbdp_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
229out:
230	kmem_free(arg_str, len);
231	return (rv);
232}
233
234/*
235 * Reset all entries to 0.
236 */
237int
238sbdp_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
252int
253sbdp_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
310static int
311sbdp_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