xref: /minix/minix/drivers/storage/fbd/action.c (revision 83133719)
1 #include <minix/drivers.h>
2 #include <sys/ioc_fbd.h>
3 #include <assert.h>
4 
5 #include "rule.h"
6 
7 /*===========================================================================*
8  *				get_rand				     *
9  *===========================================================================*/
10 static u32_t get_rand(u32_t max)
11 {
12 	/* Las Vegas algorithm for getting a random number in the range from
13 	 * 0 to max, inclusive.
14 	 */
15 	u32_t val, top;
16 
17 	/* Get an initial random number. */
18 	val = lrand48() ^ (lrand48() << 1);
19 
20 	/* Make 'max' exclusive. If it wraps, we can use the full width. */
21 	if (++max == 0) return val;
22 
23 	/* Find the largest multiple of the given range, and return a random
24 	 * number from the range, throwing away any random numbers not below
25 	 * this largest multiple.
26 	 */
27 	top = (((u32_t) -1) / max) * max;
28 
29 	while (val >= top)
30 		val = lrand48() ^ (lrand48() << 1);
31 
32 	return val % max;
33 }
34 
35 /*===========================================================================*
36  *				get_range				     *
37  *===========================================================================*/
38 static size_t get_range(struct fbd_rule *rule, u64_t pos, size_t *size,
39 	u64_t *skip)
40 {
41 	/* Compute the range within the given request range that is affected
42 	 * by the given rule, and optionally the number of bytes preceding
43 	 * the range that are also affected by the rule.
44 	 */
45 	u64_t delta;
46 	size_t off;
47 	int to_eof;
48 
49 	to_eof = rule->start >= rule->end;
50 
51 	if (pos > rule->start) {
52 		if (skip != NULL) *skip = pos - rule->start;
53 
54 		off = 0;
55 	}
56 	else {
57 		if (skip != NULL) *skip = ((u64_t)(0));
58 
59 		delta = rule->start - pos;
60 
61 		assert(ex64hi(delta) == 0);
62 
63 		off = ex64lo(delta);
64 	}
65 
66 	if (!to_eof) {
67 		assert(pos < rule->end);
68 
69 		delta = rule->end - pos;
70 
71 		if (delta < *size)
72 			*size = ex64lo(delta);
73 	}
74 
75 	assert(*size > off);
76 
77 	*size -= off;
78 
79 	return off;
80 }
81 
82 /*===========================================================================*
83  *				limit_range				     *
84  *===========================================================================*/
85 static void limit_range(iovec_t *iov, unsigned *count, size_t size)
86 {
87 	/* Limit the given vector to the given size.
88 	 */
89 	size_t chunk;
90 	int i;
91 
92 	for (i = 0; i < *count && size > 0; i++) {
93 		chunk = MIN(iov[i].iov_size, size);
94 
95 		if (chunk == size)
96 			iov[i].iov_size = size;
97 
98 		size -= chunk;
99 	}
100 
101 	*count = i;
102 }
103 
104 /*===========================================================================*
105  *				action_io_corrupt			     *
106  *===========================================================================*/
107 static void action_io_corrupt(struct fbd_rule *rule, char *buf, size_t size,
108 	u64_t pos, int UNUSED(flag))
109 {
110 	u64_t skip;
111 	u32_t val;
112 
113 	buf += get_range(rule, pos, &size, &skip);
114 
115 	switch (rule->params.corrupt.type) {
116 	case FBD_CORRUPT_ZERO:
117 		memset(buf, 0, size);
118 		break;
119 
120 	case FBD_CORRUPT_PERSIST:
121 		/* Non-dword-aligned positions and sizes are not supported;
122 		 * not by us, and not by the driver.
123 		 */
124 		if (ex64lo(pos) & (sizeof(val) - 1)) break;
125 		if (size & (sizeof(val) - 1)) break;
126 
127 		/* Consistently produce the same pattern for the same range. */
128 		val = ex64lo(skip);
129 
130 		for ( ; size >= sizeof(val); size -= sizeof(val)) {
131 			*((u32_t *) buf) = val ^ 0xdeadbeefUL;
132 
133 			val += sizeof(val);
134 			buf += sizeof(val);
135 		}
136 
137 		break;
138 
139 	case FBD_CORRUPT_RANDOM:
140 		while (size--)
141 			*buf++ = get_rand(255);
142 
143 		break;
144 
145 	default:
146 		printf("FBD: unknown corruption type %d\n",
147 			rule->params.corrupt.type);
148 	}
149 }
150 
151 /*===========================================================================*
152  *				action_pre_error			     *
153  *===========================================================================*/
154 static void action_pre_error(struct fbd_rule *rule, iovec_t *iov,
155 	unsigned *count, size_t *size, u64_t *pos)
156 {
157 	/* Limit the request to the part that precedes the matched range. */
158 	*size = get_range(rule, *pos, size, NULL);
159 
160 	limit_range(iov, count, *size);
161 }
162 
163 /*===========================================================================*
164  *				action_post_error			     *
165  *===========================================================================*/
166 static void action_post_error(struct fbd_rule *rule, size_t UNUSED(osize),
167 	int *result)
168 {
169 	/* Upon success of the first part, return the specified error code. */
170 	if (*result >= 0 && rule->params.error.code != OK)
171 		*result = rule->params.error.code;
172 }
173 
174 /*===========================================================================*
175  *				action_pre_misdir			     *
176  *===========================================================================*/
177 static void action_pre_misdir(struct fbd_rule *rule, iovec_t *UNUSED(iov),
178 	unsigned *UNUSED(count), size_t *UNUSED(size), u64_t *pos)
179 {
180 	/* Randomize the request position to fall within the range (and have
181 	 * the alignment) given by the rule.
182 	 */
183 	u32_t range, choice;
184 
185 	/* Unfortunately, we cannot interpret 0 as end as "up to end of disk"
186 	 * here, because we have no idea about the actual disk size, and the
187 	 * resulting address must of course be valid..
188 	 */
189 	range = ((rule->params.misdir.end - rule->params.misdir.start) + 1)
190 		/ rule->params.misdir.align;
191 
192 	if (range > 0)
193 		choice = get_rand(range - 1);
194 	else
195 		choice = 0;
196 
197 	*pos = rule->params.misdir.start +
198 		((u64_t)choice * rule->params.misdir.align);
199 }
200 
201 /*===========================================================================*
202  *				action_pre_losttorn			     *
203  *===========================================================================*/
204 static void action_pre_losttorn(struct fbd_rule *rule, iovec_t *iov,
205 	unsigned *count, size_t *size, u64_t *UNUSED(pos))
206 {
207 	if (*size > rule->params.losttorn.lead)
208 		*size = rule->params.losttorn.lead;
209 
210 	limit_range(iov, count, *size);
211 }
212 
213 /*===========================================================================*
214  *				action_post_losttorn			     *
215  *===========================================================================*/
216 static void action_post_losttorn(struct fbd_rule *UNUSED(rule), size_t osize,
217 	int *result)
218 {
219 	/* On success, pretend full completion. */
220 
221 	if (*result < 0) return;
222 
223 	*result = osize;
224 }
225 
226 /*===========================================================================*
227  *				action_mask				     *
228  *===========================================================================*/
229 int action_mask(struct fbd_rule *rule)
230 {
231 	/* Return the hook mask for the given rule's action type. */
232 
233 	switch (rule->action) {
234 	case FBD_ACTION_CORRUPT:	return IO_HOOK;
235 	case FBD_ACTION_ERROR:		return PRE_HOOK | POST_HOOK;
236 	case FBD_ACTION_MISDIR:		return PRE_HOOK;
237 	case FBD_ACTION_LOSTTORN:	return PRE_HOOK | POST_HOOK;
238 	default:
239 		printf("FBD: unknown action type %d\n", rule->action);
240 		return 0;
241 	}
242 }
243 
244 /*===========================================================================*
245  *				action_pre_hook				     *
246  *===========================================================================*/
247 void action_pre_hook(struct fbd_rule *rule, iovec_t *iov,
248 	unsigned *count, size_t *size, u64_t *pos)
249 {
250 	switch (rule->action) {
251 	case FBD_ACTION_ERROR:
252 		action_pre_error(rule, iov, count, size, pos);
253 		break;
254 
255 	case FBD_ACTION_MISDIR:
256 		action_pre_misdir(rule, iov, count, size, pos);
257 		break;
258 
259 	case FBD_ACTION_LOSTTORN:
260 		action_pre_losttorn(rule, iov, count, size, pos);
261 		break;
262 
263 	default:
264 		printf("FBD: bad action type %d for PRE hook\n", rule->action);
265 	}
266 }
267 
268 /*===========================================================================*
269  *				action_io_hook				     *
270  *===========================================================================*/
271 void action_io_hook(struct fbd_rule *rule, char *buf, size_t size,
272 	u64_t pos, int flag)
273 {
274 	switch (rule->action) {
275 	case FBD_ACTION_CORRUPT:
276 		action_io_corrupt(rule, buf, size, pos, flag);
277 		break;
278 
279 	default:
280 		printf("FBD: bad action type %d for IO hook\n", rule->action);
281 	}
282 }
283 
284 /*===========================================================================*
285  *				action_post_hook			     *
286  *===========================================================================*/
287 void action_post_hook(struct fbd_rule *rule, size_t osize, int *result)
288 {
289 	switch (rule->action) {
290 	case FBD_ACTION_ERROR:
291 		action_post_error(rule, osize, result);
292 		return;
293 
294 	case FBD_ACTION_LOSTTORN:
295 		action_post_losttorn(rule, osize, result);
296 		return;
297 
298 	default:
299 		printf("FBD: bad action type %d for POST hook\n",
300 			rule->action);
301 	}
302 }
303