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  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 
27 /*
28  * rseq implementation
29  */
30 
31 #include <sys/usb/clients/usbser/usbser_rseq.h>
32 
33 #ifdef _KERNEL
34 #include <sys/debug.h>
35 #include <sys/ddi.h>
36 #include <sys/sunddi.h>
37 static long rseq_random();
38 #define	random	rseq_random
39 #else
40 #include <assert.h>
41 #define	ASSERT	assert
42 #include <stdlib.h>
43 #endif
44 
45 
46 /*ARGSUSED*/
47 static int
rseq_do_common(rseq_t * rseq,int num,uintptr_t arg,int flags,int fail_err,uintptr_t fail_num)48 rseq_do_common(rseq_t *rseq, int num, uintptr_t arg, int flags, int fail_err,
49 		uintptr_t fail_num)
50 {
51 	int		i;
52 	rseq_step_t	*s;
53 	int		rval = RSEQ_OK;
54 
55 	for (i = 0; i < num; i++) {
56 		s = &rseq[i].r_do;
57 
58 		if (s->s_func == NULL) {
59 			continue;
60 		}
61 		s->s_rval = (i != fail_num) ? s->s_func(arg) : fail_err;
62 		rval = (s->s_cb) ? (s->s_cb(rseq, i, arg)) : RSEQ_OK;
63 
64 		if (rval == RSEQ_UNDO) {
65 			(void) rseq_undo(rseq, i, arg, flags);
66 			break;
67 		} else if (rval == RSEQ_ABORT) {
68 			break;
69 		}
70 		ASSERT(rval == RSEQ_OK);
71 	}
72 	return (rval);
73 }
74 
75 
76 /*ARGSUSED*/
77 static int
rseq_undo_common(rseq_t * rseq,int num,uintptr_t arg,int flags,int fail_err,uintptr_t fail_num)78 rseq_undo_common(rseq_t *rseq, int num, uintptr_t arg, int flags, int fail_err,
79 		uintptr_t fail_num)
80 {
81 	int		i;
82 	rseq_step_t	*s;
83 	int		rval = RSEQ_OK;
84 
85 	for (i = num - 1; i >= 0; i--) {
86 		s = &rseq[i].r_undo;
87 
88 		if (s->s_func == NULL) {
89 			continue;
90 		}
91 		s->s_rval = (i != fail_num) ? s->s_func(arg) : fail_err;
92 		rval = (s->s_cb) ? (s->s_cb(rseq, i, arg)) : RSEQ_OK;
93 
94 		if (rval == RSEQ_ABORT) {
95 			break;
96 		}
97 		ASSERT(rval == RSEQ_OK);
98 	}
99 	return (rval);
100 }
101 
102 
103 int
rseq_do(rseq_t * rseq,int num,uintptr_t arg,int flags)104 rseq_do(rseq_t *rseq, int num, uintptr_t arg, int flags)
105 {
106 	return (rseq_do_common(rseq, num, arg, flags, 0, -1));
107 }
108 
109 
110 int
rseq_undo(rseq_t * rseq,int num,uintptr_t arg,int flags)111 rseq_undo(rseq_t *rseq, int num, uintptr_t arg, int flags)
112 {
113 	return (rseq_undo_common(rseq, num, arg, flags, 0, -1));
114 }
115 
116 #ifdef DEBUG
117 
118 #ifndef __lock_lint
119 
120 static int
rseq_debug(rseq_t * rseq,int num,uintptr_t arg,int flags,int scenario,uintptr_t sarg1,uintptr_t sarg2,int (* func)(rseq_t *,int,uintptr_t,int,int,uintptr_t))121 rseq_debug(rseq_t *rseq, int num, uintptr_t arg, int flags, int scenario,
122 		uintptr_t sarg1, uintptr_t sarg2,
123 		int (*func)(rseq_t *, int, uintptr_t, int, int, uintptr_t))
124 {
125 	int	rnd, rval = RSEQ_OK, i;
126 
127 	switch (scenario) {
128 	case RSEQ_DBG_FAIL_ONE:
129 		rval = func(rseq, num, arg, flags, sarg1, sarg2);
130 		break;
131 	case RSEQ_DBG_FAIL_ONE_RANDOM:
132 		rnd = random() % num;
133 		rval = func(rseq, num, arg, flags, sarg1, rnd);
134 		break;
135 	case RSEQ_DBG_FAIL_ONEBYONE:
136 		for (i = 0; i < num; i++) {
137 			rval = func(rseq, num, arg, flags, sarg1, i);
138 			/*
139 			 * when aborted, the undo path is not executed, so we
140 			 * can't continue without the risk of resource leakage.
141 			 */
142 			if (rval == RSEQ_ABORT) {
143 				break;
144 			}
145 		}
146 		break;
147 	default:
148 		ASSERT(!"rseq_debug: incorrect debug scenario");
149 		rval = RSEQ_ABORT;
150 	}
151 	return (rval);
152 }
153 
154 
155 int
rseq_do_debug(rseq_t * rseq,int num,uintptr_t arg,int flags,int scenario,uintptr_t sarg1,uintptr_t sarg2)156 rseq_do_debug(rseq_t *rseq, int num, uintptr_t arg, int flags, int scenario,
157 		uintptr_t sarg1, uintptr_t sarg2)
158 {
159 	return (rseq_debug(rseq, num, arg, flags, scenario, sarg1, sarg2,
160 	    rseq_do_common));
161 }
162 
163 
164 int
rseq_undo_debug(rseq_t * rseq,int num,uintptr_t arg,int flags,int scenario,uintptr_t sarg1,uintptr_t sarg2)165 rseq_undo_debug(rseq_t *rseq, int num, uintptr_t arg, int flags, int scenario,
166 		uintptr_t sarg1, uintptr_t sarg2)
167 {
168 	return (rseq_debug(rseq, num, arg, flags, scenario, sarg1, sarg2,
169 	    rseq_undo_common));
170 }
171 
172 #ifdef _KERNEL
173 static long
rseq_random()174 rseq_random()
175 {
176 	return (ddi_get_lbolt());
177 }
178 #endif /* _KERNEL */
179 
180 #endif /* __lock_lint */
181 
182 #endif /* DEBUG */
183