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