xref: /minix/minix/drivers/storage/fbd/fbd.c (revision 7f5f010b)
1 /* Faulty Block Device (fault injection proxy), by D.C. van Moolenbroek */
2 #include <stdlib.h>
3 #include <minix/drivers.h>
4 #include <minix/blockdriver.h>
5 #include <minix/drvlib.h>
6 #include <minix/ioctl.h>
7 #include <sys/ioc_fbd.h>
8 #include <minix/ds.h>
9 #include <minix/optset.h>
10 #include <assert.h>
11 
12 #include "rule.h"
13 
14 /* Constants. */
15 #define BUF_SIZE (NR_IOREQS * CLICK_SIZE)	/* 256k */
16 
17 /* Function declarations. */
18 static int fbd_open(devminor_t minor, int access);
19 static int fbd_close(devminor_t minor);
20 static int fbd_transfer(devminor_t minor, int do_write, u64_t position,
21 	endpoint_t endpt, iovec_t *iov, unsigned int nr_req, int flags);
22 static int fbd_ioctl(devminor_t minor, unsigned long request, endpoint_t endpt,
23 	cp_grant_id_t grant, endpoint_t user_endpt);
24 
25 /* Variables. */
26 static char *fbd_buf;			/* scratch buffer */
27 
28 static char driver_label[32] = "";	/* driver DS label */
29 static devminor_t driver_minor = -1;	/* driver's partition minor to use */
30 static endpoint_t driver_endpt;		/* driver endpoint */
31 
32 /* Entry points to this driver. */
33 static struct blockdriver fbd_dtab = {
34 	.bdr_type	= BLOCKDRIVER_TYPE_OTHER,/* do not handle part. reqs */
35 	.bdr_open	= fbd_open,	/* open request, initialize device */
36 	.bdr_close	= fbd_close,	/* release device */
37 	.bdr_transfer	= fbd_transfer,	/* do the I/O */
38 	.bdr_ioctl	= fbd_ioctl	/* perform I/O control request */
39 };
40 
41 /* Options supported by this driver. */
42 static struct optset optset_table[] = {
43 	{ "label",	OPT_STRING,	driver_label,	sizeof(driver_label) },
44 	{ "minor",	OPT_INT,	&driver_minor,	10		     },
45 	{ NULL,		0,		NULL,		0		     }
46 };
47 
48 /*===========================================================================*
49  *				sef_cb_init_fresh			     *
50  *===========================================================================*/
51 static int sef_cb_init_fresh(int type, sef_init_info_t *UNUSED(info))
52 {
53 	clock_t uptime;
54 	int r;
55 
56 	/* Parse the given parameters. */
57 	if (env_argc > 1)
58 		optset_parse(optset_table, env_argv[1]);
59 
60 	if (driver_label[0] == '\0')
61 		panic("no driver label given");
62 
63 	if (ds_retrieve_label_endpt(driver_label, &driver_endpt))
64 		panic("unable to resolve driver label");
65 
66 	if (driver_minor > 255)
67 		panic("no or invalid driver minor given");
68 
69 #if DEBUG
70 	printf("FBD: driver label '%s' (endpt %d), minor %d\n",
71 		driver_label, driver_endpt, driver_minor);
72 #endif
73 
74 	/* Initialize resources. */
75 	fbd_buf = alloc_contig(BUF_SIZE, 0, NULL);
76 
77 	assert(fbd_buf != NULL);
78 
79 	if ((r = getticks(&uptime)) != OK)
80 		panic("getuptime failed (%d)\n", r);
81 
82 	srand48(uptime);
83 
84 	/* Announce we are up! */
85 	blockdriver_announce(type);
86 
87 	return OK;
88 }
89 
90 /*===========================================================================*
91  *				sef_cb_signal_handler			     *
92  *===========================================================================*/
93 static void sef_cb_signal_handler(int signo)
94 {
95 	/* Terminate immediately upon receiving a SIGTERM. */
96 	if (signo != SIGTERM) return;
97 
98 #if DEBUG
99 	printf("FBD: shutting down\n");
100 #endif
101 
102 	/* Clean up resources. */
103 	free_contig(fbd_buf, BUF_SIZE);
104 
105 	exit(0);
106 }
107 
108 /*===========================================================================*
109  *				sef_local_startup			     *
110  *===========================================================================*/
111 static void sef_local_startup(void)
112 {
113 	/* Register init callbacks. */
114 	sef_setcb_init_fresh(sef_cb_init_fresh);
115 	sef_setcb_init_restart(sef_cb_init_fresh);
116 	sef_setcb_init_lu(sef_cb_init_fresh);
117 
118 	/* Register signal callback. */
119 	sef_setcb_signal_handler(sef_cb_signal_handler);
120 
121 	/* Let SEF perform startup. */
122 	sef_startup();
123 }
124 
125 /*===========================================================================*
126  *				main					     *
127  *===========================================================================*/
128 int main(int argc, char **argv)
129 {
130 	/* SEF local startup. */
131 	env_setargs(argc, argv);
132 	sef_local_startup();
133 
134 	/* Call the generic receive loop. */
135 	blockdriver_task(&fbd_dtab);
136 
137 	return OK;
138 }
139 
140 /*===========================================================================*
141  *				fbd_open				     *
142  *===========================================================================*/
143 static int fbd_open(devminor_t UNUSED(minor), int access)
144 {
145 	/* Open a device. */
146 	message m;
147 	int r;
148 
149 	/* We simply forward this request to the real driver. */
150 	memset(&m, 0, sizeof(m));
151 	m.m_type = BDEV_OPEN;
152 	m.m_lbdev_lblockdriver_msg.minor = driver_minor;
153 	m.m_lbdev_lblockdriver_msg.access = access;
154 	m.m_lbdev_lblockdriver_msg.id = 0;
155 
156 	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
157 		panic("ipc_sendrec to driver failed (%d)\n", r);
158 
159 	if (m.m_type != BDEV_REPLY)
160 		panic("invalid reply from driver (%d)\n", m.m_type);
161 
162 	return m.m_lblockdriver_lbdev_reply.status;
163 }
164 
165 /*===========================================================================*
166  *				fbd_close				     *
167  *===========================================================================*/
168 static int fbd_close(devminor_t UNUSED(minor))
169 {
170 	/* Close a device. */
171 	message m;
172 	int r;
173 
174 	/* We simply forward this request to the real driver. */
175 	memset(&m, 0, sizeof(m));
176 	m.m_type = BDEV_CLOSE;
177 	m.m_lbdev_lblockdriver_msg.minor = driver_minor;
178 	m.m_lbdev_lblockdriver_msg.id = 0;
179 
180 	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
181 		panic("ipc_sendrec to driver failed (%d)\n", r);
182 
183 	if (m.m_type != BDEV_REPLY)
184 		panic("invalid reply from driver (%d)\n", m.m_type);
185 
186 	return m.m_lblockdriver_lbdev_reply.status;
187 }
188 
189 /*===========================================================================*
190  *				fbd_ioctl				     *
191  *===========================================================================*/
192 static int fbd_ioctl(devminor_t UNUSED(minor), unsigned long request,
193 	endpoint_t endpt, cp_grant_id_t grant, endpoint_t UNUSED(user_endpt))
194 {
195 	/* Handle an I/O control request. */
196 	cp_grant_id_t gid;
197 	message m;
198 	int r;
199 
200 	/* We only handle the FBD requests, and pass on everything else. */
201 	switch (request) {
202 	case FBDCADDRULE:
203 	case FBDCDELRULE:
204 	case FBDCGETRULE:
205 		return rule_ctl(request, endpt, grant);
206 	}
207 
208 	assert(grant != GRANT_INVALID);
209 
210 	gid = cpf_grant_indirect(driver_endpt, endpt, grant);
211 	assert(gid != GRANT_INVALID);
212 
213 	memset(&m, 0, sizeof(m));
214 	m.m_type = BDEV_IOCTL;
215 	m.m_lbdev_lblockdriver_msg.minor = driver_minor;
216 	m.m_lbdev_lblockdriver_msg.request = request;
217 	m.m_lbdev_lblockdriver_msg.grant = gid;
218 	m.m_lbdev_lblockdriver_msg.user = NONE;
219 	m.m_lbdev_lblockdriver_msg.id = 0;
220 
221 	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
222 		panic("ipc_sendrec to driver failed (%d)\n", r);
223 
224 	if (m.m_type != BDEV_REPLY)
225 		panic("invalid reply from driver (%d)\n", m.m_type);
226 
227 	cpf_revoke(gid);
228 
229 	return m.m_lblockdriver_lbdev_reply.status;
230 }
231 
232 /*===========================================================================*
233  *				fbd_transfer_direct			     *
234  *===========================================================================*/
235 static ssize_t fbd_transfer_direct(int do_write, u64_t position,
236 	endpoint_t endpt, iovec_t *iov, unsigned int count, int flags)
237 {
238 	/* Forward the entire transfer request, without any intervention. */
239 	iovec_s_t iovec[NR_IOREQS];
240 	cp_grant_id_t grant;
241 	message m;
242 	int i, r;
243 
244 	for (i = 0; i < count; i++) {
245 		iovec[i].iov_size = iov[i].iov_size;
246 		iovec[i].iov_grant = cpf_grant_indirect(driver_endpt, endpt,
247 			iov[i].iov_addr);
248 		assert(iovec[i].iov_grant != GRANT_INVALID);
249 	}
250 
251 	grant = cpf_grant_direct(driver_endpt, (vir_bytes) iovec,
252 		count * sizeof(iovec[0]), CPF_READ);
253 	assert(grant != GRANT_INVALID);
254 
255 	m.m_type = do_write ? BDEV_SCATTER : BDEV_GATHER;
256 	m.m_lbdev_lblockdriver_msg.minor = driver_minor;
257 	m.m_lbdev_lblockdriver_msg.count = count;
258 	m.m_lbdev_lblockdriver_msg.grant = grant;
259 	m.m_lbdev_lblockdriver_msg.flags = flags;
260 	m.m_lbdev_lblockdriver_msg.id = 0;
261 	m.m_lbdev_lblockdriver_msg.pos = position;
262 
263 	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
264 		panic("ipc_sendrec to driver failed (%d)\n", r);
265 
266 	if (m.m_type != BDEV_REPLY)
267 		panic("invalid reply from driver (%d)\n", m.m_type);
268 
269 	cpf_revoke(grant);
270 
271 	for (i = 0; i < count; i++)
272 		cpf_revoke(iovec[i].iov_grant);
273 
274 	return m.m_lblockdriver_lbdev_reply.status;
275 }
276 
277 /*===========================================================================*
278  *				fbd_transfer_copy			     *
279  *===========================================================================*/
280 static ssize_t fbd_transfer_copy(int do_write, u64_t position,
281 	endpoint_t endpt, iovec_t *iov, unsigned int count, size_t size,
282 	int flags)
283 {
284 	/* Interpose on the request. */
285 	iovec_s_t iovec[NR_IOREQS];
286 	struct vscp_vec vscp_vec[SCPVEC_NR];
287 	cp_grant_id_t grant;
288 	size_t off, len;
289 	message m;
290 	char *ptr;
291 	int i, j, r;
292 	ssize_t rsize;
293 
294 	assert(count > 0 && count <= SCPVEC_NR);
295 
296 	if (size > BUF_SIZE) {
297 		printf("FBD: allocating memory for %d bytes\n", size);
298 
299 		ptr = alloc_contig(size, 0, NULL);
300 
301 		assert(ptr != NULL);
302 	}
303 	else ptr = fbd_buf;
304 
305 	/* For write operations, first copy in the data to write. */
306 	if (do_write) {
307 		for (i = off = 0; i < count; i++) {
308 			len = iov[i].iov_size;
309 
310 			vscp_vec[i].v_from = endpt;
311 			vscp_vec[i].v_to = SELF;
312 			vscp_vec[i].v_gid = iov[i].iov_addr;
313 			vscp_vec[i].v_offset = 0;
314 			vscp_vec[i].v_addr = (vir_bytes) (ptr + off);
315 			vscp_vec[i].v_bytes = len;
316 
317 			off += len;
318 		}
319 
320 		if ((r = sys_vsafecopy(vscp_vec, i)) != OK)
321 			panic("vsafecopy failed (%d)\n", r);
322 
323 		/* Trigger write hook. */
324 		rule_io_hook(ptr, size, position, FBD_FLAG_WRITE);
325 	}
326 
327 	/* Allocate grants for the data, in the same chunking as the original
328 	 * vector. This avoids performance fluctuations with bad hardware as
329 	 * observed with the filter driver.
330 	 */
331 	for (i = off = 0; i < count; i++) {
332 		len = iov[i].iov_size;
333 
334 		iovec[i].iov_size = len;
335 		iovec[i].iov_grant = cpf_grant_direct(driver_endpt,
336 			(vir_bytes) (ptr + off), len,
337 			do_write ? CPF_READ : CPF_WRITE);
338 		assert(iovec[i].iov_grant != GRANT_INVALID);
339 
340 		off += len;
341 	}
342 
343 	grant = cpf_grant_direct(driver_endpt, (vir_bytes) iovec,
344 		count * sizeof(iovec[0]), CPF_READ);
345 	assert(grant != GRANT_INVALID);
346 
347 	m.m_type = do_write ? BDEV_SCATTER : BDEV_GATHER;
348 	m.m_lbdev_lblockdriver_msg.minor = driver_minor;
349 	m.m_lbdev_lblockdriver_msg.count = count;
350 	m.m_lbdev_lblockdriver_msg.grant = grant;
351 	m.m_lbdev_lblockdriver_msg.flags = flags;
352 	m.m_lbdev_lblockdriver_msg.id = 0;
353 	m.m_lbdev_lblockdriver_msg.pos = position;
354 
355 	if ((r = ipc_sendrec(driver_endpt, &m)) != OK)
356 		panic("ipc_sendrec to driver failed (%d)\n", r);
357 
358 	if (m.m_type != BDEV_REPLY)
359 		panic("invalid reply from driver (%d)\n", m.m_type);
360 
361 	cpf_revoke(grant);
362 
363 	for (i = 0; i < count; i++)
364 		cpf_revoke(iovec[i].iov_grant);
365 
366 	/* For read operations, finish by copying out the data read. */
367 	if (!do_write) {
368 		/* Trigger read hook. */
369 		rule_io_hook(ptr, size, position, FBD_FLAG_READ);
370 
371 		/* Upon success, copy back whatever has been processed. */
372 		rsize = m.m_lblockdriver_lbdev_reply.status;
373 		for (i = j = off = 0; rsize > 0 && i < count; i++) {
374 			len = MIN(rsize, iov[i].iov_size);
375 
376 			vscp_vec[j].v_from = SELF;
377 			vscp_vec[j].v_to = endpt;
378 			vscp_vec[j].v_gid = iov[i].iov_addr;
379 			vscp_vec[j].v_offset = 0;
380 			vscp_vec[j].v_addr = (vir_bytes) (ptr + off);
381 			vscp_vec[j].v_bytes = len;
382 
383 			off += len;
384 			rsize -= len;
385 			j++;
386 		}
387 
388 		if (j > 0 && (r = sys_vsafecopy(vscp_vec, j)) != OK)
389 			panic("vsafecopy failed (%d)\n", r);
390 	}
391 
392 	if (ptr != fbd_buf)
393 		free_contig(ptr, size);
394 
395 	return m.m_lblockdriver_lbdev_reply.status;
396 }
397 
398 /*===========================================================================*
399  *				fbd_transfer				     *
400  *===========================================================================*/
401 static int fbd_transfer(devminor_t UNUSED(minor), int do_write, u64_t position,
402 	endpoint_t endpt, iovec_t *iov, unsigned int nr_req, int flags)
403 {
404 	/* Transfer data from or to the device. */
405 	unsigned int count;
406 	size_t size, osize;
407 	int i, hooks;
408 	ssize_t r;
409 
410 	/* Compute the total size of the request. */
411 	for (size = i = 0; i < nr_req; i++)
412 		size += iov[i].iov_size;
413 
414 	osize = size;
415 	count = nr_req;
416 
417 	hooks = rule_find(position, size,
418 		do_write ? FBD_FLAG_WRITE : FBD_FLAG_READ);
419 
420 #if DEBUG
421 	printf("FBD: %s operation for pos %"PRIx64" size %u -> hooks %x\n",
422 		do_write ? "write" : "read", position, size, hooks);
423 #endif
424 
425 	if (hooks & PRE_HOOK)
426 		rule_pre_hook(iov, &count, &size, &position);
427 
428 	if (count > 0) {
429 		if (hooks & IO_HOOK) {
430 			r = fbd_transfer_copy(do_write, position, endpt, iov,
431 				count, size, flags);
432 		} else {
433 			r = fbd_transfer_direct(do_write, position, endpt, iov,
434 				count, flags);
435 		}
436 	}
437 	else r = 0;
438 
439 	if (hooks & POST_HOOK)
440 		rule_post_hook(osize, &r);
441 
442 #if DEBUG
443 	printf("FBD: returning %d\n", r);
444 #endif
445 
446 	return r;
447 }
448