103831d35Sstevel /*
203831d35Sstevel * CDDL HEADER START
303831d35Sstevel *
403831d35Sstevel * The contents of this file are subject to the terms of the
503831d35Sstevel * Common Development and Distribution License (the "License").
603831d35Sstevel * You may not use this file except in compliance with the License.
703831d35Sstevel *
803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
903831d35Sstevel * or http://www.opensolaris.org/os/licensing.
1003831d35Sstevel * See the License for the specific language governing permissions
1103831d35Sstevel * and limitations under the License.
1203831d35Sstevel *
1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each
1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the
1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying
1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner]
1803831d35Sstevel *
1903831d35Sstevel * CDDL HEADER END
2003831d35Sstevel */
2103831d35Sstevel
2203831d35Sstevel /*
23*d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
2403831d35Sstevel * Use is subject to license terms.
2503831d35Sstevel */
2603831d35Sstevel
2703831d35Sstevel #include <sys/types.h>
2803831d35Sstevel #include <sys/param.h>
2903831d35Sstevel #include <sys/sunddi.h>
3003831d35Sstevel #include <sys/note.h>
3103831d35Sstevel #include <sys/promif.h>
3203831d35Sstevel #include <sys/sbdp_error.h>
3303831d35Sstevel #include <sys/sbdp_priv.h>
3403831d35Sstevel
3503831d35Sstevel /*
3603831d35Sstevel * The purpose if this model is to make it easy to inject error at all
3703831d35Sstevel * decision making points, such that all code paths can be tested, and
3803831d35Sstevel * states arrived are expected and recoverable.
3903831d35Sstevel *
4003831d35Sstevel * Passthru command "inject-error" will be used for injecting
4103831d35Sstevel * errors. A typical error injection command will look like the
4203831d35Sstevel * following:
4303831d35Sstevel *
4403831d35Sstevel * cfgadm -x passthru -o inject-error=func_name:entry_point:value N0.SB0
4503831d35Sstevel *
4603831d35Sstevel * where "func_name" is the name of the function where error will be
4703831d35Sstevel * injected, "entry_point" is a number in the function to identify which
4803831d35Sstevel * decision making point it is that we are injecting error, and "value"
4903831d35Sstevel * is what we want the check to return. The last field is ignored,
5003831d35Sstevel * so it can be any valid attachment point.
5103831d35Sstevel *
5203831d35Sstevel * For example, if we want to inject error at the 3rd entry in function
5303831d35Sstevel * sbdp_disconnect_cpu (we start counting at 0), we will issue the
5403831d35Sstevel * following command:
5503831d35Sstevel *
5603831d35Sstevel * cfgadm -x passthru -o inject-error=sbdp_disconnect_cpu:3:-1 N0.SB0
5703831d35Sstevel *
5803831d35Sstevel * To clear the error, change the value to 0, or whatever success
5903831d35Sstevel * corresponds to in the particular function.
6003831d35Sstevel *
6103831d35Sstevel * cfgadm -x passthru -o inject-error=sbdp_disconnect_cpu:3:0 N0.SB0
6203831d35Sstevel *
6303831d35Sstevel * Since this command is mainly for debugging, not all illegal options
6403831d35Sstevel * are rejected. Non-digit strings are accepted for entry point and
6503831d35Sstevel * value. They will be translated to 0.
6603831d35Sstevel *
6703831d35Sstevel * Another passthru command "reset-error" is used to clear all errors
6803831d35Sstevel * that have been injected. The only argument it needs is a valid
6903831d35Sstevel * attachment point as the last field.
7003831d35Sstevel *
7103831d35Sstevel * NOTE: Once implemented, the error injection points should remain
7203831d35Sstevel * relatively stable as QA will be using them for testing.
7303831d35Sstevel */
7403831d35Sstevel
7503831d35Sstevel
7603831d35Sstevel /*
7703831d35Sstevel * Variable that controls if error injection should be done or not
7803831d35Sstevel */
7903831d35Sstevel #ifdef DEBUG
8003831d35Sstevel uint_t sbdp_do_inject = 1;
8103831d35Sstevel
8203831d35Sstevel /*
8303831d35Sstevel * Different error injection types that sbdp_ie_type can be
8403831d35Sstevel */
8503831d35Sstevel #define SBDP_IE_RANDOM 0 /* Returns randomly 0 or -1 */
8603831d35Sstevel #define SBDP_IE_FAILURE 1 /* Returns -1 */
8703831d35Sstevel #define SBDP_IE_DEFINED 2 /* Returns value from sbdp_error_matrix */
8803831d35Sstevel
8903831d35Sstevel /*
9003831d35Sstevel * Variable that controls what type of error injection to do
9103831d35Sstevel */
9203831d35Sstevel int sbdp_ie_type = SBDP_IE_DEFINED;
9303831d35Sstevel
9403831d35Sstevel /*
9503831d35Sstevel * Basic return values from sbdp_inject_error
9603831d35Sstevel */
9703831d35Sstevel #define SUCCESS 0
9803831d35Sstevel #define FAILURE -1
9903831d35Sstevel
10003831d35Sstevel /*
10103831d35Sstevel * Maximum number of error injection entry points
10203831d35Sstevel */
10303831d35Sstevel #define SBDP_IE_MAX_ENTRIES 4
10403831d35Sstevel
10503831d35Sstevel typedef struct error_matrix {
10603831d35Sstevel const char *func_name;
10703831d35Sstevel uint_t num_entries;
10803831d35Sstevel int entries[SBDP_IE_MAX_ENTRIES];
10903831d35Sstevel } error_matrix_t;
11003831d35Sstevel
11103831d35Sstevel static error_matrix_t sbdp_error_matrix[] = {
11203831d35Sstevel { "sbdp_disconnect_cpu", 3, 0, 0, 0, 0 },
11303831d35Sstevel { "sbdp_connect_cpu", 3, 0, 0, 0, 0 },
11403831d35Sstevel { "sbdp_cpu_poweron", 2, 0, 0, 0, 0 },
11503831d35Sstevel { "sbdp_cpu_poweroff", 4, 0, 0, 0, 0 },
11603831d35Sstevel /* Termination entry, must exist */
11703831d35Sstevel { NULL, 0, 0, 0, 0, 0 },
11803831d35Sstevel };
11903831d35Sstevel
12003831d35Sstevel static int sbdp_func_lookup(const char *func_name);
12103831d35Sstevel
12203831d35Sstevel extern int sbdp_strtoi(char *p, char **pos);
12303831d35Sstevel
12403831d35Sstevel /*
12503831d35Sstevel * sbdp error injector. The argument should be of the following format:
12603831d35Sstevel *
12703831d35Sstevel * inject_error=func_str:entry_str:value_str
12803831d35Sstevel *
12903831d35Sstevel * Returns ESBD_INVAL if arg is not of the above format,
13003831d35Sstevel * or if any of the fields are invalid.
13103831d35Sstevel *
13203831d35Sstevel * Returns ESBD_NOERROR after setting the correct entry in the error
13303831d35Sstevel * matrix to the value passed in.
13403831d35Sstevel */
13503831d35Sstevel int
sbdp_passthru_inject_error(sbdp_handle_t * hp,void * arg)13603831d35Sstevel sbdp_passthru_inject_error(sbdp_handle_t *hp, void *arg)
13703831d35Sstevel {
13803831d35Sstevel _NOTE(ARGUNUSED(hp))
13903831d35Sstevel
14003831d35Sstevel char *arg_str, *func_str, *entry_str, *value_str;
14103831d35Sstevel int index, value;
14203831d35Sstevel size_t len = strlen(arg) + 1;
14303831d35Sstevel uint_t entry;
14403831d35Sstevel int rv = ESBD_NOERROR;
14503831d35Sstevel static char *f = "sbdp_passthru_inject_error";
14603831d35Sstevel
14703831d35Sstevel arg_str = kmem_alloc(len, KM_SLEEP);
14803831d35Sstevel (void) strcpy(arg_str, arg);
14903831d35Sstevel
15003831d35Sstevel /*
15103831d35Sstevel * Find '=' in the argument. Return ESBD_INVAL if '=' is
15203831d35Sstevel * not found.
15303831d35Sstevel */
15403831d35Sstevel if ((func_str = strchr(arg_str, '=')) == NULL) {
15503831d35Sstevel rv = ESBD_INVAL;
15603831d35Sstevel goto out;
15703831d35Sstevel }
15803831d35Sstevel
15903831d35Sstevel /*
16003831d35Sstevel * Now func_str points to '=' in arg_str. Increment the pointer
16103831d35Sstevel * so it points to the begining of the function string.
16203831d35Sstevel * Find the first ':' in the argument. Return ESBD_INVAL if
16303831d35Sstevel * not found.
16403831d35Sstevel */
16503831d35Sstevel if ((entry_str = strchr(++func_str, ':')) == NULL) {
16603831d35Sstevel rv = ESBD_INVAL;
16703831d35Sstevel goto out;
16803831d35Sstevel }
16903831d35Sstevel
17003831d35Sstevel /*
17103831d35Sstevel * Now entry_str points to the first ':' in arg_str. Set it
17203831d35Sstevel * to '\0' to NULL terminate func_str. Increment the
17303831d35Sstevel * pointer so it points to the begining of the entry string.
17403831d35Sstevel */
17503831d35Sstevel *entry_str++ = '\0';
17603831d35Sstevel
17703831d35Sstevel /*
17803831d35Sstevel * Now entry_str points to the begining of the entry string.
17903831d35Sstevel * Find the next ':' in the argument. Return ESBD_INVAL if
18003831d35Sstevel * ':' is not found.
18103831d35Sstevel */
18203831d35Sstevel if ((value_str = strchr(entry_str, ':')) == NULL) {
18303831d35Sstevel rv = ESBD_INVAL;
18403831d35Sstevel goto out;
18503831d35Sstevel }
18603831d35Sstevel
18703831d35Sstevel /*
18803831d35Sstevel * Now value_str points to the second ':' in arg_str. Set it
18903831d35Sstevel * to '\0' to NULL terminate entry_str. Increment the
19003831d35Sstevel * pointer so it points to the begining of the value string.
19103831d35Sstevel * The rest of the arg_str is taken as the value string.
19203831d35Sstevel */
19303831d35Sstevel *value_str++ = '\0';
19403831d35Sstevel
19503831d35Sstevel /*
19603831d35Sstevel * Find this function in the matrix. Return ESBD_INVAL if
19703831d35Sstevel * the function name is not found.
19803831d35Sstevel */
19903831d35Sstevel if ((index = sbdp_func_lookup(func_str)) == -1) {
20003831d35Sstevel rv = ESBD_INVAL;
20103831d35Sstevel goto out;
20203831d35Sstevel }
20303831d35Sstevel
20403831d35Sstevel /*
20503831d35Sstevel * To reduce the amount of code we have to write, we tolerate
20603831d35Sstevel * non-number input for entry point, and translate it to 0.
20703831d35Sstevel */
20803831d35Sstevel entry = (uint_t)sbdp_strtoi(entry_str, NULL);
20903831d35Sstevel
21003831d35Sstevel if (entry >= sbdp_error_matrix[index].num_entries) {
21103831d35Sstevel rv = ESBD_INVAL;
21203831d35Sstevel goto out;
21303831d35Sstevel }
21403831d35Sstevel
21503831d35Sstevel /*
21603831d35Sstevel * No checking for value. Non-number string will be translated
21703831d35Sstevel * to 0.
21803831d35Sstevel */
21903831d35Sstevel value = sbdp_strtoi(value_str, NULL);
22003831d35Sstevel
22103831d35Sstevel SBDP_DBG_ERR("%s: index = %d, entry = %d, value = %d\n",
22203831d35Sstevel f, index, entry, value);
22303831d35Sstevel
22403831d35Sstevel /*
22503831d35Sstevel * Set value at the right entry.
22603831d35Sstevel */
22703831d35Sstevel sbdp_error_matrix[index].entries[entry] = value;
22803831d35Sstevel
22903831d35Sstevel out:
23003831d35Sstevel kmem_free(arg_str, len);
23103831d35Sstevel return (rv);
23203831d35Sstevel }
23303831d35Sstevel
23403831d35Sstevel /*
23503831d35Sstevel * Reset all entries to 0.
23603831d35Sstevel */
23703831d35Sstevel int
sbdp_passthru_reset_error(sbdp_handle_t * hp,void * arg)23803831d35Sstevel sbdp_passthru_reset_error(sbdp_handle_t *hp, void *arg)
23903831d35Sstevel {
24003831d35Sstevel _NOTE(ARGUNUSED(hp))
24103831d35Sstevel _NOTE(ARGUNUSED(arg))
24203831d35Sstevel
24303831d35Sstevel uint_t i, j;
24403831d35Sstevel
24503831d35Sstevel for (i = 0; sbdp_error_matrix[i].func_name != NULL; i++)
24603831d35Sstevel for (j = 0; j < SBDP_IE_MAX_ENTRIES; j++)
24703831d35Sstevel sbdp_error_matrix[i].entries[j] = 0;
24803831d35Sstevel
24903831d35Sstevel return (ESBD_NOERROR);
25003831d35Sstevel }
25103831d35Sstevel
25203831d35Sstevel int
sbdp_inject_error(const char * func_name,uint_t entry)25303831d35Sstevel sbdp_inject_error(const char *func_name, uint_t entry)
25403831d35Sstevel {
255*d3d50737SRafael Vanoni extern clock_t ddi_get_lbolt(void);
25603831d35Sstevel int index;
25703831d35Sstevel int value;
25803831d35Sstevel static char *f = "sbdp_inject_error";
25903831d35Sstevel
26003831d35Sstevel if (sbdp_do_inject == 0)
26103831d35Sstevel return (SUCCESS);
26203831d35Sstevel
26303831d35Sstevel switch (sbdp_ie_type) {
26403831d35Sstevel
26503831d35Sstevel case SBDP_IE_RANDOM:
26603831d35Sstevel /*
26703831d35Sstevel * Since we usually only need a binary type of return
26803831d35Sstevel * value, use lbolt to generate the psuedo random
26903831d35Sstevel * response.
27003831d35Sstevel */
271*d3d50737SRafael Vanoni value = (-(int)(ddi_get_lbolt() % 2));
27203831d35Sstevel break;
27303831d35Sstevel
27403831d35Sstevel case SBDP_IE_FAILURE:
27503831d35Sstevel value = FAILURE;
27603831d35Sstevel break;
27703831d35Sstevel
27803831d35Sstevel case SBDP_IE_DEFINED:
27903831d35Sstevel /*
28003831d35Sstevel * Don't inject error if can't find the function.
28103831d35Sstevel */
28203831d35Sstevel if ((index = sbdp_func_lookup(func_name)) == -1) {
28303831d35Sstevel value = SUCCESS;
28403831d35Sstevel break;
28503831d35Sstevel }
28603831d35Sstevel
28703831d35Sstevel /*
28803831d35Sstevel * Don't inject error if can't find the entry.
28903831d35Sstevel */
29003831d35Sstevel if (entry >= sbdp_error_matrix[index].num_entries) {
29103831d35Sstevel value = SUCCESS;
29203831d35Sstevel break;
29303831d35Sstevel }
29403831d35Sstevel
29503831d35Sstevel value = sbdp_error_matrix[index].entries[entry];
29603831d35Sstevel break;
29703831d35Sstevel
29803831d35Sstevel default:
29903831d35Sstevel value = SUCCESS;
30003831d35Sstevel break;
30103831d35Sstevel }
30203831d35Sstevel
30303831d35Sstevel if (value != SUCCESS)
30403831d35Sstevel SBDP_DBG_ERR("%s: function=%s entry=%d value=%d\n",
30503831d35Sstevel f, func_name, entry, value);
30603831d35Sstevel
30703831d35Sstevel return (value);
30803831d35Sstevel }
30903831d35Sstevel
31003831d35Sstevel static int
sbdp_func_lookup(const char * func_name)31103831d35Sstevel sbdp_func_lookup(const char *func_name)
31203831d35Sstevel {
31303831d35Sstevel int i;
31403831d35Sstevel const char *name;
31503831d35Sstevel
31603831d35Sstevel /*
31703831d35Sstevel * Linear search for a match
31803831d35Sstevel */
31903831d35Sstevel for (i = 0; (name = sbdp_error_matrix[i].func_name) != NULL; i++) {
32003831d35Sstevel if (strcmp(name, func_name) == 0)
32103831d35Sstevel return (i);
32203831d35Sstevel }
32303831d35Sstevel
32403831d35Sstevel /*
32503831d35Sstevel * Function name not found in matrix
32603831d35Sstevel */
32703831d35Sstevel return (-1);
32803831d35Sstevel }
32903831d35Sstevel
33003831d35Sstevel #endif /* DEBUG */
331