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 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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 *===========================================================================*/ 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