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