1 /* libbdev - block device interfacing library, by D.C. van Moolenbroek */ 2 3 #include <minix/drivers.h> 4 #include <minix/bdev.h> 5 #include <minix/ioctl.h> 6 #include <assert.h> 7 8 #include "const.h" 9 #include "type.h" 10 #include "proto.h" 11 12 void bdev_driver(dev_t dev, char *label) 13 { 14 /* Associate a driver with the given (major) device, using its endpoint. 15 * File system usage note: typically called from mount and newdriver. 16 */ 17 static int first = TRUE; 18 19 if (first) { 20 /* Initialize the driver endpoint array. */ 21 bdev_driver_init(); 22 23 first = FALSE; 24 } 25 26 bdev_update(dev, label); 27 } 28 29 static int bdev_retry(int *driver_tries, int *transfer_tries, int *result) 30 { 31 /* Return TRUE iff the call result implies that we should retry the operation. 32 */ 33 34 switch (*result) { 35 case ERESTART: 36 /* We get this error internally if the driver has restarted and the 37 * current operation may now go through. Check the retry count for 38 * driver restarts first, as we don't want to keep trying forever. 39 */ 40 if (++*driver_tries < DRIVER_TRIES) 41 return TRUE; 42 43 *result = EDEADSRCDST; 44 45 break; 46 47 case EIO: 48 /* The 'transfer_tries' pointer is non-NULL if this was a transfer 49 * request. If we get back an I/O failure, keep retrying the request 50 * until we hit the transfer retry limit. 51 */ 52 if (transfer_tries != NULL && ++*transfer_tries < TRANSFER_TRIES) 53 return TRUE; 54 55 break; 56 } 57 58 return FALSE; 59 } 60 61 static int bdev_opcl(int req, dev_t dev, int access) 62 { 63 /* Open or close the given minor device. 64 */ 65 message m; 66 int r, driver_tries = 0; 67 68 do { 69 memset(&m, 0, sizeof(m)); 70 m.m_type = req; 71 m.m_lbdev_lblockdriver_msg.minor = minor(dev); 72 m.m_lbdev_lblockdriver_msg.access = access; 73 74 r = bdev_sendrec(dev, &m); 75 } while (bdev_retry(&driver_tries, NULL, &r)); 76 77 return r; 78 } 79 80 int bdev_open(dev_t dev, int access) 81 { 82 /* Open the given minor device. 83 * File system usage note: typically called from mount, after bdev_driver. 84 */ 85 int r; 86 87 r = bdev_opcl(BDEV_OPEN, dev, access); 88 89 if (r == OK) 90 bdev_minor_add(dev, access); 91 92 return r; 93 } 94 95 int bdev_close(dev_t dev) 96 { 97 /* Close the given minor device. 98 * File system usage note: typically called from unmount. 99 */ 100 int r; 101 102 bdev_flush_asyn(dev); 103 104 r = bdev_opcl(BDEV_CLOSE, dev, 0); 105 106 if (r == OK) 107 bdev_minor_del(dev); 108 109 return r; 110 } 111 112 static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf, 113 size_t count, int flags, message *m) 114 { 115 /* Set up a single-buffer read/write request. 116 */ 117 endpoint_t endpt; 118 cp_grant_id_t grant; 119 int access; 120 121 assert((ssize_t) count >= 0); 122 123 if ((endpt = bdev_driver_get(dev)) == NONE) 124 return EDEADSRCDST; 125 126 access = (req == BDEV_READ) ? CPF_WRITE : CPF_READ; 127 128 grant = cpf_grant_direct(endpt, (vir_bytes) buf, count, access); 129 130 if (!GRANT_VALID(grant)) { 131 printf("bdev: unable to allocate grant!\n"); 132 return EINVAL; 133 } 134 135 memset(m, 0, sizeof(*m)); 136 m->m_type = req; 137 m->m_lbdev_lblockdriver_msg.minor = minor(dev); 138 m->m_lbdev_lblockdriver_msg.pos = pos; 139 m->m_lbdev_lblockdriver_msg.count = count; 140 m->m_lbdev_lblockdriver_msg.grant = grant; 141 m->m_lbdev_lblockdriver_msg.flags = flags; 142 143 return OK; 144 } 145 146 static void bdev_rdwt_cleanup(const message *m) 147 { 148 /* Clean up a single-buffer read/write request. 149 */ 150 151 cpf_revoke(m->m_lbdev_lblockdriver_msg.grant); 152 } 153 154 static ssize_t bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf, 155 size_t count, int flags) 156 { 157 /* Perform a synchronous read or write call using a single buffer. 158 */ 159 message m; 160 int r, driver_tries = 0, transfer_tries = 0; 161 162 do { 163 if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &m)) != OK) 164 break; 165 166 r = bdev_sendrec(dev, &m); 167 168 bdev_rdwt_cleanup(&m); 169 } while (bdev_retry(&driver_tries, &transfer_tries, &r)); 170 171 return r; 172 } 173 174 static int bdev_vrdwt_setup(int req, dev_t dev, u64_t pos, iovec_t *vec, 175 int count, int flags, message *m, iovec_s_t *gvec) 176 { 177 /* Set up a vectored read/write request. 178 */ 179 ssize_t size; 180 endpoint_t endpt; 181 cp_grant_id_t grant; 182 int i, access; 183 184 assert(count <= NR_IOREQS); 185 186 if ((endpt = bdev_driver_get(dev)) == NONE) 187 return EDEADSRCDST; 188 189 access = (req == BDEV_GATHER) ? CPF_WRITE : CPF_READ; 190 size = 0; 191 192 for (i = 0; i < count; i++) { 193 grant = cpf_grant_direct(endpt, vec[i].iov_addr, vec[i].iov_size, 194 access); 195 196 if (!GRANT_VALID(grant)) { 197 printf("bdev: unable to allocate grant!\n"); 198 199 for (i--; i >= 0; i--) 200 cpf_revoke(gvec[i].iov_grant); 201 202 return EINVAL; 203 } 204 205 gvec[i].iov_grant = grant; 206 gvec[i].iov_size = vec[i].iov_size; 207 208 assert(vec[i].iov_size > 0); 209 assert((ssize_t) (size + vec[i].iov_size) > size); 210 211 size += vec[i].iov_size; 212 } 213 214 grant = cpf_grant_direct(endpt, (vir_bytes) gvec, sizeof(gvec[0]) * count, 215 CPF_READ); 216 217 if (!GRANT_VALID(grant)) { 218 printf("bdev: unable to allocate grant!\n"); 219 220 for (i = count - 1; i >= 0; i--) 221 cpf_revoke(gvec[i].iov_grant); 222 223 return EINVAL; 224 } 225 226 memset(m, 0, sizeof(*m)); 227 m->m_type = req; 228 m->m_lbdev_lblockdriver_msg.minor = minor(dev); 229 m->m_lbdev_lblockdriver_msg.pos = pos; 230 m->m_lbdev_lblockdriver_msg.count = count; 231 m->m_lbdev_lblockdriver_msg.grant = grant; 232 m->m_lbdev_lblockdriver_msg.flags = flags; 233 234 return OK; 235 } 236 237 static void bdev_vrdwt_cleanup(const message *m, iovec_s_t *gvec) 238 { 239 /* Clean up a vectored read/write request. 240 */ 241 cp_grant_id_t grant; 242 int i; 243 244 grant = m->m_lbdev_lblockdriver_msg.grant; 245 246 cpf_revoke(grant); 247 248 for (i = m->m_lbdev_lblockdriver_msg.count - 1; i >= 0; i--) 249 cpf_revoke(gvec[i].iov_grant); 250 } 251 252 static ssize_t bdev_vrdwt(int req, dev_t dev, u64_t pos, iovec_t *vec, 253 int count, int flags) 254 { 255 /* Perform a synchronous read or write call using a vector of buffers. 256 */ 257 iovec_s_t gvec[NR_IOREQS]; 258 message m; 259 int r, driver_tries = 0, transfer_tries = 0; 260 261 do { 262 if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &m, 263 gvec)) != OK) 264 break; 265 266 r = bdev_sendrec(dev, &m); 267 268 bdev_vrdwt_cleanup(&m, gvec); 269 } while (bdev_retry(&driver_tries, &transfer_tries, &r)); 270 271 return r; 272 } 273 274 ssize_t bdev_read(dev_t dev, u64_t pos, char *buf, size_t count, int flags) 275 { 276 /* Perform a synchronous read call into a single buffer. 277 */ 278 279 return bdev_rdwt(BDEV_READ, dev, pos, buf, count, flags); 280 } 281 282 ssize_t bdev_write(dev_t dev, u64_t pos, char *buf, size_t count, int flags) 283 { 284 /* Perform a synchronous write call from a single buffer. 285 */ 286 287 return bdev_rdwt(BDEV_WRITE, dev, pos, buf, count, flags); 288 } 289 290 ssize_t bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags) 291 { 292 /* Perform a synchronous read call into a vector of buffers. 293 */ 294 295 return bdev_vrdwt(BDEV_GATHER, dev, pos, vec, count, flags); 296 } 297 298 ssize_t bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags) 299 { 300 /* Perform a synchronous write call from a vector of buffers. 301 */ 302 303 return bdev_vrdwt(BDEV_SCATTER, dev, pos, vec, count, flags); 304 } 305 306 static int bdev_ioctl_setup(dev_t dev, int request, void *buf, 307 endpoint_t user_endpt, message *m) 308 { 309 /* Set up an I/O control request. 310 */ 311 endpoint_t endpt; 312 size_t size; 313 cp_grant_id_t grant; 314 int access; 315 316 if ((endpt = bdev_driver_get(dev)) == NONE) 317 return EDEADSRCDST; 318 319 if (_MINIX_IOCTL_BIG(request)) 320 size = _MINIX_IOCTL_SIZE_BIG(request); 321 else 322 size = _MINIX_IOCTL_SIZE(request); 323 324 access = 0; 325 if (_MINIX_IOCTL_IOR(request)) access |= CPF_WRITE; 326 if (_MINIX_IOCTL_IOW(request)) access |= CPF_READ; 327 328 /* The size may be 0, in which case 'buf' need not be a valid pointer. */ 329 grant = cpf_grant_direct(endpt, (vir_bytes) buf, size, access); 330 331 if (!GRANT_VALID(grant)) { 332 printf("bdev: unable to allocate grant!\n"); 333 return EINVAL; 334 } 335 336 memset(m, 0, sizeof(*m)); 337 m->m_type = BDEV_IOCTL; 338 m->m_lbdev_lblockdriver_msg.minor = minor(dev); 339 m->m_lbdev_lblockdriver_msg.request = request; 340 m->m_lbdev_lblockdriver_msg.grant = grant; 341 m->m_lbdev_lblockdriver_msg.user = user_endpt; 342 343 return OK; 344 } 345 346 static void bdev_ioctl_cleanup(const message *m) 347 { 348 /* Clean up an I/O control request. 349 */ 350 351 cpf_revoke(m->m_lbdev_lblockdriver_msg.grant); 352 } 353 354 int bdev_ioctl(dev_t dev, int request, void *buf, endpoint_t user_endpt) 355 { 356 /* Perform a synchronous I/O control request. 357 */ 358 message m; 359 int r, driver_tries = 0; 360 361 do { 362 if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt, &m)) != OK) 363 break; 364 365 r = bdev_sendrec(dev, &m); 366 367 bdev_ioctl_cleanup(&m); 368 } while (bdev_retry(&driver_tries, NULL, &r)); 369 370 return r; 371 } 372 373 void bdev_flush_asyn(dev_t dev) 374 { 375 /* Flush all ongoing asynchronous requests to the given minor device. This 376 * involves blocking until all I/O for it has completed. 377 * File system usage note: typically called from flush. 378 */ 379 bdev_call_t *call; 380 381 while ((call = bdev_call_find(dev)) != NULL) 382 (void) bdev_wait_asyn(call->id); 383 } 384 385 static bdev_id_t bdev_rdwt_asyn(int req, dev_t dev, u64_t pos, char *buf, 386 size_t count, int flags, bdev_callback_t callback, bdev_param_t param) 387 { 388 /* Perform an asynchronous read or write call using a single buffer. 389 */ 390 bdev_call_t *call; 391 int r; 392 393 if ((call = bdev_call_alloc(1)) == NULL) 394 return ENOMEM; 395 396 if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &call->msg)) != 397 OK) { 398 bdev_call_free(call); 399 400 return r; 401 } 402 403 if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) { 404 bdev_rdwt_cleanup(&call->msg); 405 406 bdev_call_free(call); 407 408 return r; 409 } 410 411 call->dev = dev; 412 call->callback = callback; 413 call->param = param; 414 call->driver_tries = 0; 415 call->transfer_tries = 0; 416 call->vec[0].iov_addr = (vir_bytes) buf; 417 call->vec[0].iov_size = count; 418 419 return call->id; 420 } 421 422 static bdev_id_t bdev_vrdwt_asyn(int req, dev_t dev, u64_t pos, iovec_t *vec, 423 int count, int flags, bdev_callback_t callback, bdev_param_t param) 424 { 425 /* Perform an asynchronous read or write call using a vector of buffers. 426 */ 427 bdev_call_t *call; 428 int r; 429 430 if ((call = bdev_call_alloc(count)) == NULL) 431 return ENOMEM; 432 433 if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &call->msg, 434 call->gvec)) != OK) { 435 bdev_call_free(call); 436 437 return r; 438 } 439 440 if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) { 441 bdev_vrdwt_cleanup(&call->msg, call->gvec); 442 443 bdev_call_free(call); 444 445 return r; 446 } 447 448 call->dev = dev; 449 call->callback = callback; 450 call->param = param; 451 call->driver_tries = 0; 452 call->transfer_tries = 0; 453 memcpy(call->vec, vec, sizeof(vec[0]) * count); 454 455 return call->id; 456 } 457 458 bdev_id_t bdev_read_asyn(dev_t dev, u64_t pos, char *buf, size_t count, 459 int flags, bdev_callback_t callback, bdev_param_t param) 460 { 461 /* Perform an asynchronous read call into a single buffer. 462 */ 463 464 return bdev_rdwt_asyn(BDEV_READ, dev, pos, buf, count, flags, callback, 465 param); 466 } 467 468 bdev_id_t bdev_write_asyn(dev_t dev, u64_t pos, char *buf, size_t count, 469 int flags, bdev_callback_t callback, bdev_param_t param) 470 { 471 /* Perform an asynchronous write call from a single buffer. 472 */ 473 474 return bdev_rdwt_asyn(BDEV_WRITE, dev, pos, buf, count, flags, callback, 475 param); 476 } 477 478 bdev_id_t bdev_gather_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count, 479 int flags, bdev_callback_t callback, bdev_param_t param) 480 { 481 /* Perform an asynchronous read call into a vector of buffers. 482 */ 483 484 return bdev_vrdwt_asyn(BDEV_GATHER, dev, pos, vec, count, flags, callback, 485 param); 486 } 487 488 bdev_id_t bdev_scatter_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count, 489 int flags, bdev_callback_t callback, bdev_param_t param) 490 { 491 /* Perform an asynchronous write call into a vector of buffers. 492 */ 493 494 return bdev_vrdwt_asyn(BDEV_SCATTER, dev, pos, vec, count, flags, callback, 495 param); 496 } 497 498 bdev_id_t bdev_ioctl_asyn(dev_t dev, int request, void *buf, 499 endpoint_t user_endpt, bdev_callback_t callback, bdev_param_t param) 500 { 501 /* Perform an asynchronous I/O control request. 502 */ 503 bdev_call_t *call; 504 int r; 505 506 if ((call = bdev_call_alloc(1)) == NULL) 507 return ENOMEM; 508 509 if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt, 510 &call->msg)) != OK) { 511 bdev_call_free(call); 512 513 return r; 514 } 515 516 if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) { 517 bdev_ioctl_cleanup(&call->msg); 518 519 bdev_call_free(call); 520 521 return r; 522 } 523 524 call->dev = dev; 525 call->callback = callback; 526 call->param = param; 527 call->driver_tries = 0; 528 call->vec[0].iov_addr = (vir_bytes) buf; 529 530 return call->id; 531 } 532 533 void bdev_callback_asyn(bdev_call_t *call, int result) 534 { 535 /* Perform the callback for an asynchronous request, with the given result. 536 * Clean up the call structure afterwards. 537 */ 538 539 /* If this was a transfer request and the result is EIO, we may want to retry 540 * the request first. 541 */ 542 switch (call->msg.m_type) { 543 case BDEV_READ: 544 case BDEV_WRITE: 545 case BDEV_GATHER: 546 case BDEV_SCATTER: 547 if (result == EIO && ++call->transfer_tries < TRANSFER_TRIES) { 548 result = bdev_senda(call->dev, &call->msg, call->id); 549 550 if (result == OK) 551 return; 552 } 553 } 554 555 /* Clean up. */ 556 switch (call->msg.m_type) { 557 case BDEV_READ: 558 case BDEV_WRITE: 559 bdev_rdwt_cleanup(&call->msg); 560 561 break; 562 563 case BDEV_GATHER: 564 case BDEV_SCATTER: 565 bdev_vrdwt_cleanup(&call->msg, call->gvec); 566 567 break; 568 569 case BDEV_IOCTL: 570 bdev_ioctl_cleanup(&call->msg); 571 572 break; 573 574 default: 575 assert(0); 576 } 577 578 /* Call the callback function. */ 579 /* FIXME: we assume all reasonable ssize_t values can be stored in an int. */ 580 call->callback(call->dev, call->id, call->param, result); 581 582 /* Free up the call structure. */ 583 bdev_call_free(call); 584 } 585 586 int bdev_restart_asyn(bdev_call_t *call) 587 { 588 /* The driver for the given call has restarted, and may now have a new 589 * endpoint. Recreate and resend the request for the given call. 590 */ 591 int type, r = OK; 592 593 /* Update and check the retry limit for driver restarts first. */ 594 if (++call->driver_tries >= DRIVER_TRIES) 595 return EDEADSRCDST; 596 597 /* Recreate all grants for the new endpoint. */ 598 type = call->msg.m_type; 599 600 switch (type) { 601 case BDEV_READ: 602 case BDEV_WRITE: 603 bdev_rdwt_cleanup(&call->msg); 604 605 r = bdev_rdwt_setup(type, call->dev, 606 call->msg.m_lbdev_lblockdriver_msg.pos, 607 (char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.count, 608 call->msg.m_lbdev_lblockdriver_msg.flags, &call->msg); 609 610 break; 611 612 case BDEV_GATHER: 613 case BDEV_SCATTER: 614 bdev_vrdwt_cleanup(&call->msg, call->gvec); 615 616 r = bdev_vrdwt_setup(type, call->dev, 617 call->msg.m_lbdev_lblockdriver_msg.pos, 618 call->vec, call->msg.m_lbdev_lblockdriver_msg.count, call->msg.m_lbdev_lblockdriver_msg.flags, 619 &call->msg, call->gvec); 620 621 break; 622 623 case BDEV_IOCTL: 624 bdev_ioctl_cleanup(&call->msg); 625 626 r = bdev_ioctl_setup(call->dev, call->msg.m_lbdev_lblockdriver_msg.request, 627 (char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.user, 628 &call->msg); 629 630 break; 631 632 default: 633 assert(0); 634 } 635 636 if (r != OK) 637 return r; 638 639 /* Try to resend the request. */ 640 return bdev_senda(call->dev, &call->msg, call->id); 641 } 642