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