1 /* When a needed block is not in the cache, it must be fetched from the disk. 2 * Special character files also require I/O. The routines for these are here. 3 * 4 * The entry points in this file are: 5 * cdev_open: open a character device 6 * cdev_close: close a character device 7 * cdev_io: initiate a read, write, or ioctl to a character device 8 * cdev_select: initiate a select call on a device 9 * cdev_cancel: cancel an I/O request, blocking until it has been cancelled 10 * cdev_reply: process the result of a character driver request 11 * bdev_open: open a block device 12 * bdev_close: close a block device 13 * bdev_reply: process the result of a block driver request 14 * bdev_up: a block driver has been mapped in 15 * do_ioctl: perform the IOCTL system call 16 */ 17 18 #include "fs.h" 19 #include <string.h> 20 #include <fcntl.h> 21 #include <assert.h> 22 #include <sys/stat.h> 23 #include <sys/ttycom.h> 24 #include <minix/callnr.h> 25 #include <minix/com.h> 26 #include <minix/endpoint.h> 27 #include <minix/ioctl.h> 28 #include <minix/u64.h> 29 #include "file.h" 30 #include "scratchpad.h" 31 #include "dmap.h" 32 #include <minix/vfsif.h> 33 #include "vnode.h" 34 #include "vmnt.h" 35 36 static int cdev_opcl(int op, dev_t dev, int flags); 37 static int block_io(endpoint_t driver_e, message *mess_ptr); 38 static cp_grant_id_t make_grant(endpoint_t driver_e, endpoint_t user_e, int op, 39 vir_bytes buf, unsigned long size); 40 41 /*===========================================================================* 42 * bdev_open * 43 *===========================================================================*/ 44 int bdev_open(dev_t dev, int access) 45 { 46 /* Open a block device. */ 47 devmajor_t major_dev; 48 devminor_t minor_dev; 49 message dev_mess; 50 int r; 51 52 major_dev = major(dev); 53 minor_dev = minor(dev); 54 if (major_dev < 0 || major_dev >= NR_DEVICES) return ENXIO; 55 if (dmap[major_dev].dmap_driver == NONE) return ENXIO; 56 57 memset(&dev_mess, 0, sizeof(dev_mess)); 58 dev_mess.m_type = BDEV_OPEN; 59 dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev; 60 dev_mess.m_lbdev_lblockdriver_msg.access = 0; 61 if (access & R_BIT) dev_mess.m_lbdev_lblockdriver_msg.access |= BDEV_R_BIT; 62 if (access & W_BIT) dev_mess.m_lbdev_lblockdriver_msg.access |= BDEV_W_BIT; 63 dev_mess.m_lbdev_lblockdriver_msg.id = 0; 64 65 /* Call the task. */ 66 r = block_io(dmap[major_dev].dmap_driver, &dev_mess); 67 if (r != OK) 68 return r; 69 70 return dev_mess.m_lblockdriver_lbdev_reply.status; 71 } 72 73 74 /*===========================================================================* 75 * bdev_close * 76 *===========================================================================*/ 77 int bdev_close(dev_t dev) 78 { 79 /* Close a block device. */ 80 devmajor_t major_dev; 81 devminor_t minor_dev; 82 message dev_mess; 83 int r; 84 85 major_dev = major(dev); 86 minor_dev = minor(dev); 87 if (major_dev < 0 || major_dev >= NR_DEVICES) return ENXIO; 88 if (dmap[major_dev].dmap_driver == NONE) return ENXIO; 89 90 memset(&dev_mess, 0, sizeof(dev_mess)); 91 dev_mess.m_type = BDEV_CLOSE; 92 dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev; 93 dev_mess.m_lbdev_lblockdriver_msg.id = 0; 94 95 r = block_io(dmap[major_dev].dmap_driver, &dev_mess); 96 if (r != OK) 97 return r; 98 99 return dev_mess.m_lblockdriver_lbdev_reply.status; 100 } 101 102 103 /*===========================================================================* 104 * bdev_ioctl * 105 *===========================================================================*/ 106 static int bdev_ioctl(dev_t dev, endpoint_t proc_e, unsigned long req, 107 vir_bytes buf) 108 { 109 /* Perform an I/O control operation on a block device. */ 110 struct dmap *dp; 111 cp_grant_id_t gid; 112 message dev_mess; 113 devmajor_t major_dev; 114 devminor_t minor_dev; 115 int r; 116 117 major_dev = major(dev); 118 minor_dev = minor(dev); 119 120 /* Determine task dmap. */ 121 dp = &dmap[major_dev]; 122 if (dp->dmap_driver == NONE) { 123 printf("VFS: bdev_ioctl: no driver for major %d\n", major_dev); 124 return(ENXIO); 125 } 126 127 /* Set up a grant if necessary. */ 128 gid = make_grant(dp->dmap_driver, proc_e, BDEV_IOCTL, buf, req); 129 130 /* Set up the message passed to the task. */ 131 memset(&dev_mess, 0, sizeof(dev_mess)); 132 133 dev_mess.m_type = BDEV_IOCTL; 134 dev_mess.m_lbdev_lblockdriver_msg.minor = minor_dev; 135 dev_mess.m_lbdev_lblockdriver_msg.request = req; 136 dev_mess.m_lbdev_lblockdriver_msg.grant = gid; 137 dev_mess.m_lbdev_lblockdriver_msg.user = proc_e; 138 dev_mess.m_lbdev_lblockdriver_msg.id = 0; 139 140 /* Call the task. */ 141 r = block_io(dp->dmap_driver, &dev_mess); 142 143 /* Clean up. */ 144 if (GRANT_VALID(gid)) cpf_revoke(gid); 145 146 /* Return the result. */ 147 if (r != OK) 148 return(r); 149 150 return(dev_mess.m_lblockdriver_lbdev_reply.status); 151 } 152 153 154 /*===========================================================================* 155 * make_grant * 156 *===========================================================================*/ 157 static cp_grant_id_t make_grant(endpoint_t driver_e, endpoint_t user_e, int op, 158 vir_bytes buf, unsigned long bytes) 159 { 160 /* Create a magic grant for the given operation and buffer. */ 161 cp_grant_id_t gid; 162 int access; 163 size_t size; 164 165 switch (op) { 166 case CDEV_READ: 167 case CDEV_WRITE: 168 gid = cpf_grant_magic(driver_e, user_e, buf, 169 (size_t) bytes, op == CDEV_READ ? CPF_WRITE : CPF_READ); 170 break; 171 172 case CDEV_IOCTL: 173 case BDEV_IOCTL: 174 /* For IOCTLs, the bytes parameter contains the IOCTL request. 175 * This request encodes the requested access method and buffer size. 176 */ 177 access = 0; 178 if(_MINIX_IOCTL_IOR(bytes)) access |= CPF_WRITE; 179 if(_MINIX_IOCTL_IOW(bytes)) access |= CPF_READ; 180 if(_MINIX_IOCTL_BIG(bytes)) 181 size = _MINIX_IOCTL_SIZE_BIG(bytes); 182 else 183 size = _MINIX_IOCTL_SIZE(bytes); 184 185 /* Grant access to the buffer even if no I/O happens with the ioctl, 186 * although now that we no longer identify responses based on grants, 187 * this is not strictly necessary. 188 */ 189 gid = cpf_grant_magic(driver_e, user_e, buf, size, access); 190 break; 191 192 default: 193 panic("VFS: unknown operation %d", op); 194 } 195 196 if (!GRANT_VALID(gid)) 197 panic("VFS: cpf_grant_magic failed"); 198 199 return gid; 200 } 201 202 /*===========================================================================* 203 * cdev_map * 204 *===========================================================================*/ 205 dev_t cdev_map(dev_t dev, struct fproc *rfp) 206 { 207 /* Map the given device number to a real device number, remapping /dev/tty to 208 * the given process's controlling terminal if it has one. Perform a bounds 209 * check on the resulting device's major number, and return NO_DEV on failure. 210 * This function is idempotent but not used that way. 211 */ 212 devmajor_t major; 213 214 /* First cover one special case: /dev/tty, the magic device that translates 215 * to the controlling tty. 216 */ 217 if ((major = major(dev)) == CTTY_MAJOR) { 218 /* No controlling terminal? Fail the request. */ 219 if (rfp->fp_tty == NO_DEV) return NO_DEV; 220 221 /* Substitute the controlling terminal device. */ 222 dev = rfp->fp_tty; 223 major = major(dev); 224 } 225 226 if (major < 0 || major >= NR_DEVICES) return NO_DEV; 227 228 return dev; 229 } 230 231 /*===========================================================================* 232 * cdev_get * 233 *===========================================================================*/ 234 static struct dmap *cdev_get(dev_t dev, devminor_t *minor_dev) 235 { 236 /* Obtain the dmap structure for the given device, if a valid driver exists for 237 * the major device. Perform redirection for CTTY_MAJOR. 238 */ 239 struct dmap *dp; 240 int slot; 241 242 /* Remap /dev/tty as needed. Perform a bounds check on the major number. */ 243 if ((dev = cdev_map(dev, fp)) == NO_DEV) 244 return(NULL); 245 246 /* Determine task dmap. */ 247 dp = &dmap[major(dev)]; 248 249 /* See if driver is roughly valid. */ 250 if (dp->dmap_driver == NONE) return(NULL); 251 252 if (isokendpt(dp->dmap_driver, &slot) != OK) { 253 printf("VFS: cdev_get: old driver for major %x (%d)\n", major(dev), 254 dp->dmap_driver); 255 return(NULL); 256 } 257 258 /* Also return the (possibly redirected) minor number. */ 259 *minor_dev = minor(dev); 260 return dp; 261 } 262 263 /*===========================================================================* 264 * cdev_io * 265 *===========================================================================*/ 266 int cdev_io( 267 int op, /* CDEV_READ, CDEV_WRITE, or CDEV_IOCTL */ 268 dev_t dev, /* major-minor device number */ 269 endpoint_t proc_e, /* in whose address space is buf? */ 270 vir_bytes buf, /* virtual address of the buffer */ 271 off_t pos, /* byte position */ 272 unsigned long bytes, /* how many bytes to transfer, or request */ 273 int flags /* special flags, like O_NONBLOCK */ 274 ) 275 { 276 /* Initiate a read, write, or ioctl to a character device. */ 277 devminor_t minor_dev; 278 struct dmap *dp; 279 message dev_mess; 280 cp_grant_id_t gid; 281 int r; 282 283 assert(op == CDEV_READ || op == CDEV_WRITE || op == CDEV_IOCTL); 284 285 /* Determine task map. */ 286 if ((dp = cdev_get(dev, &minor_dev)) == NULL) 287 return(EIO); 288 289 /* Handle TIOCSCTTY ioctl: set controlling tty. 290 * TODO: cleaner implementation work in progress. 291 */ 292 if (op == CDEV_IOCTL && bytes == TIOCSCTTY && major(dev) == TTY_MAJOR) { 293 fp->fp_tty = dev; 294 } 295 296 /* Create a grant for the buffer provided by the user process. */ 297 gid = make_grant(dp->dmap_driver, proc_e, op, buf, bytes); 298 299 /* Set up the rest of the message that will be sent to the driver. */ 300 memset(&dev_mess, 0, sizeof(dev_mess)); 301 dev_mess.m_type = op; 302 dev_mess.m_vfs_lchardriver_readwrite.minor = minor_dev; 303 if (op == CDEV_IOCTL) { 304 dev_mess.m_vfs_lchardriver_readwrite.request = bytes; 305 dev_mess.m_vfs_lchardriver_readwrite.user = proc_e; 306 } else { 307 dev_mess.m_vfs_lchardriver_readwrite.pos = pos; 308 dev_mess.m_vfs_lchardriver_readwrite.count = bytes; 309 } 310 dev_mess.m_vfs_lchardriver_readwrite.id = proc_e; 311 dev_mess.m_vfs_lchardriver_readwrite.grant = gid; 312 dev_mess.m_vfs_lchardriver_readwrite.flags = 0; 313 if (flags & O_NONBLOCK) 314 dev_mess.m_vfs_lchardriver_readwrite.flags |= CDEV_NONBLOCK; 315 316 /* Send the request to the driver. */ 317 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 318 panic("VFS: asynsend in cdev_io failed: %d", r); 319 320 /* Suspend the calling process until a reply arrives. */ 321 wait_for(dp->dmap_driver); 322 assert(!GRANT_VALID(fp->fp_grant)); 323 fp->fp_grant = gid; /* revoke this when unsuspended. */ 324 325 return SUSPEND; 326 } 327 328 329 /*===========================================================================* 330 * cdev_clone * 331 *===========================================================================*/ 332 static int cdev_clone(dev_t dev, devminor_t new_minor) 333 { 334 /* A new minor device number has been returned. Request PFS to create a 335 * temporary device file to hold it. 336 */ 337 struct vnode *vp; 338 struct node_details res; 339 int r; 340 341 /* Device number of the new device. */ 342 dev = makedev(major(dev), new_minor); 343 344 /* Issue request */ 345 r = req_newnode(PFS_PROC_NR, fp->fp_effuid, fp->fp_effgid, 346 ALL_MODES | I_CHAR_SPECIAL, dev, &res); 347 if (r != OK) { 348 (void) cdev_opcl(CDEV_CLOSE, dev, 0); 349 return r; 350 } 351 352 /* Drop old node and use the new values */ 353 if ((vp = get_free_vnode()) == NULL) { 354 req_putnode(PFS_PROC_NR, res.inode_nr, 1); /* is this right? */ 355 (void) cdev_opcl(CDEV_CLOSE, dev, 0); 356 return(err_code); 357 } 358 lock_vnode(vp, VNODE_OPCL); 359 360 assert(fp->fp_filp[scratch(fp).file.fd_nr] != NULL); 361 unlock_vnode(fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno); 362 put_vnode(fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno); 363 364 vp->v_fs_e = res.fs_e; 365 vp->v_vmnt = NULL; 366 vp->v_dev = NO_DEV; 367 vp->v_fs_e = res.fs_e; 368 vp->v_inode_nr = res.inode_nr; 369 vp->v_mode = res.fmode; 370 vp->v_sdev = dev; 371 vp->v_fs_count = 1; 372 vp->v_ref_count = 1; 373 fp->fp_filp[scratch(fp).file.fd_nr]->filp_vno = vp; 374 375 return OK; 376 } 377 378 379 /*===========================================================================* 380 * cdev_opcl * 381 *===========================================================================*/ 382 static int cdev_opcl( 383 int op, /* operation, CDEV_OPEN or CDEV_CLOSE */ 384 dev_t dev, /* device to open or close */ 385 int flags /* mode bits and flags */ 386 ) 387 { 388 /* Open or close a character device. */ 389 devminor_t minor_dev, new_minor; 390 struct dmap *dp; 391 struct fproc *rfp; 392 message dev_mess; 393 int r, r2; 394 395 assert(op == CDEV_OPEN || op == CDEV_CLOSE); 396 397 /* Determine task dmap. */ 398 if ((dp = cdev_get(dev, &minor_dev)) == NULL) 399 return(ENXIO); 400 401 /* CTTY exception: do not actually send the open/close request for /dev/tty 402 * to the driver. This avoids the case that the actual device will remain 403 * open forever if the process calls setsid() after opening /dev/tty. 404 */ 405 if (major(dev) == CTTY_MAJOR) return(OK); 406 407 /* Add O_NOCTTY to the access flags if this process is not a session leader, 408 * or if it already has a controlling tty, or if it is someone else's 409 * controlling tty. For performance reasons, only search the full process 410 * table if this driver has set controlling ttys before. 411 */ 412 if (!(fp->fp_flags & FP_SESLDR) || fp->fp_tty != 0) { 413 flags |= O_NOCTTY; 414 } else if (!(flags & O_NOCTTY) && dp->dmap_seen_tty) { 415 for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) 416 if (rfp->fp_pid != PID_FREE && rfp->fp_tty == dev) 417 flags |= O_NOCTTY; 418 } 419 420 /* Prepare the request message. */ 421 memset(&dev_mess, 0, sizeof(dev_mess)); 422 423 dev_mess.m_type = op; 424 dev_mess.m_vfs_lchardriver_openclose.minor = minor_dev; 425 dev_mess.m_vfs_lchardriver_openclose.id = who_e; 426 if (op == CDEV_OPEN) { 427 dev_mess.m_vfs_lchardriver_openclose.user = who_e; 428 dev_mess.m_vfs_lchardriver_openclose.access = 0; 429 if (flags & R_BIT) 430 dev_mess.m_vfs_lchardriver_openclose.access |= CDEV_R_BIT; 431 if (flags & W_BIT) 432 dev_mess.m_vfs_lchardriver_openclose.access |= CDEV_W_BIT; 433 if (flags & O_NOCTTY) 434 dev_mess.m_vfs_lchardriver_openclose.access |= CDEV_NOCTTY; 435 } 436 437 /* Send the request to the driver. */ 438 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 439 panic("VFS: asynsend in cdev_opcl failed: %d", r); 440 441 /* Block the thread waiting for a reply. */ 442 fp->fp_task = dp->dmap_driver; 443 self->w_task = dp->dmap_driver; 444 self->w_drv_sendrec = &dev_mess; 445 446 worker_wait(); 447 448 self->w_task = NONE; 449 self->w_drv_sendrec = NULL; 450 451 /* Process the reply. */ 452 r = dev_mess.m_lchardriver_vfs_reply.status; 453 454 if (op == CDEV_OPEN && r >= 0) { 455 /* Some devices need special processing upon open. Such a device is 456 * "cloned", i.e. on a succesful open it is replaced by a new device 457 * with a new unique minor device number. This new device number 458 * identifies a new object (such as a new network connection) that has 459 * been allocated within a driver. 460 */ 461 if (r & CDEV_CLONED) { 462 new_minor = r & ~(CDEV_CLONED | CDEV_CTTY); 463 if ((r2 = cdev_clone(dev, new_minor)) < 0) 464 return(r2); 465 } 466 467 /* Did this call make the tty the controlling tty? */ 468 if (r & CDEV_CTTY) { 469 fp->fp_tty = dev; 470 dp->dmap_seen_tty = TRUE; 471 } 472 473 r = OK; 474 } 475 476 /* Return the result from the driver. */ 477 return(r); 478 } 479 480 481 /*===========================================================================* 482 * cdev_open * 483 *===========================================================================*/ 484 int cdev_open(dev_t dev, int flags) 485 { 486 /* Open a character device. */ 487 488 return cdev_opcl(CDEV_OPEN, dev, flags); 489 } 490 491 492 /*===========================================================================* 493 * cdev_close * 494 *===========================================================================*/ 495 int cdev_close(dev_t dev) 496 { 497 /* Close a character device. */ 498 499 return cdev_opcl(CDEV_CLOSE, dev, 0); 500 } 501 502 503 /*===========================================================================* 504 * do_ioctl * 505 *===========================================================================*/ 506 int do_ioctl(void) 507 { 508 /* Perform the ioctl(2) system call. */ 509 unsigned long ioctlrequest; 510 int r = OK; 511 struct filp *f; 512 register struct vnode *vp; 513 dev_t dev; 514 vir_bytes argx; 515 516 scratch(fp).file.fd_nr = job_m_in.m_lc_vfs_ioctl.fd; 517 ioctlrequest = job_m_in.m_lc_vfs_ioctl.req; 518 argx = (vir_bytes)job_m_in.m_lc_vfs_ioctl.arg; 519 520 if ((f = get_filp(scratch(fp).file.fd_nr, VNODE_READ)) == NULL) 521 return(err_code); 522 vp = f->filp_vno; /* get vnode pointer */ 523 if (!S_ISCHR(vp->v_mode) && !S_ISBLK(vp->v_mode)) { 524 r = ENOTTY; 525 } 526 527 if (r == OK) { 528 dev = vp->v_sdev; 529 530 if (S_ISBLK(vp->v_mode)) { 531 f->filp_ioctl_fp = fp; 532 533 r = bdev_ioctl(dev, who_e, ioctlrequest, argx); 534 535 f->filp_ioctl_fp = NULL; 536 } else 537 r = cdev_io(CDEV_IOCTL, dev, who_e, argx, 0, ioctlrequest, 538 f->filp_flags); 539 } 540 541 unlock_filp(f); 542 543 return(r); 544 } 545 546 547 /*===========================================================================* 548 * cdev_select * 549 *===========================================================================*/ 550 int cdev_select(dev_t dev, int ops) 551 { 552 /* Initiate a select call on a device. Return OK iff the request was sent. 553 * This function explicitly bypasses cdev_get() since it must not do CTTY 554 * mapping, because a) the caller already has done that, b) "fp" may be wrong. 555 */ 556 devmajor_t major; 557 message dev_mess; 558 struct dmap *dp; 559 int r; 560 561 /* Determine task dmap, without CTTY mapping. */ 562 assert(dev != NO_DEV); 563 major = major(dev); 564 assert(major >= 0 && major < NR_DEVICES); 565 assert(major != CTTY_MAJOR); 566 dp = &dmap[major]; 567 568 /* Prepare the request message. */ 569 memset(&dev_mess, 0, sizeof(dev_mess)); 570 571 dev_mess.m_type = CDEV_SELECT; 572 dev_mess.m_vfs_lchardriver_select.minor = minor(dev); 573 dev_mess.m_vfs_lchardriver_select.ops = ops; 574 575 /* Send the request to the driver. */ 576 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 577 panic("VFS: asynsend in cdev_select failed: %d", r); 578 579 return(OK); 580 } 581 582 583 /*===========================================================================* 584 * cdev_cancel * 585 *===========================================================================*/ 586 int cdev_cancel(dev_t dev) 587 { 588 /* Cancel an I/O request, blocking until it has been cancelled. */ 589 devminor_t minor_dev; 590 message dev_mess; 591 struct dmap *dp; 592 int r; 593 594 /* Determine task dmap. */ 595 if ((dp = cdev_get(dev, &minor_dev)) == NULL) 596 return(EIO); 597 598 /* Prepare the request message. */ 599 memset(&dev_mess, 0, sizeof(dev_mess)); 600 601 dev_mess.m_type = CDEV_CANCEL; 602 dev_mess.m_vfs_lchardriver_cancel.minor = minor_dev; 603 dev_mess.m_vfs_lchardriver_cancel.id = fp->fp_endpoint; 604 605 /* Send the request to the driver. */ 606 if ((r = asynsend3(dp->dmap_driver, &dev_mess, AMF_NOREPLY)) != OK) 607 panic("VFS: asynsend in cdev_cancel failed: %d", r); 608 609 /* Suspend this thread until we have received the response. */ 610 fp->fp_task = dp->dmap_driver; 611 self->w_task = dp->dmap_driver; 612 self->w_drv_sendrec = &dev_mess; 613 614 worker_wait(); 615 616 self->w_task = NONE; 617 self->w_drv_sendrec = NULL; 618 619 /* Clean up and return the result (note: the request may have completed). */ 620 if (GRANT_VALID(fp->fp_grant)) { 621 (void) cpf_revoke(fp->fp_grant); 622 fp->fp_grant = GRANT_INVALID; 623 } 624 625 r = dev_mess.m_lchardriver_vfs_reply.status; 626 return (r == EAGAIN) ? EINTR : r; 627 } 628 629 630 /*===========================================================================* 631 * block_io * 632 *===========================================================================*/ 633 static int block_io(endpoint_t driver_e, message *mess_ptr) 634 { 635 /* Perform I/O on a block device. The current thread is suspended until a reply 636 * comes in from the driver. 637 */ 638 int r, status, retry_count; 639 message mess_retry; 640 641 assert(IS_BDEV_RQ(mess_ptr->m_type)); 642 mess_retry = *mess_ptr; 643 retry_count = 0; 644 645 do { 646 r = drv_sendrec(driver_e, mess_ptr); 647 if (r != OK) 648 return r; 649 650 status = mess_ptr->m_lblockdriver_lbdev_reply.status; 651 if (status == ERESTART) { 652 r = EDEADEPT; 653 *mess_ptr = mess_retry; 654 retry_count++; 655 } 656 } while (status == ERESTART && retry_count < 5); 657 658 /* If we failed to restart the request, return EIO */ 659 if (status == ERESTART && retry_count >= 5) 660 return EIO; 661 662 if (r != OK) { 663 if (r == EDEADSRCDST || r == EDEADEPT) { 664 printf("VFS: dead driver %d\n", driver_e); 665 dmap_unmap_by_endpt(driver_e); 666 return(EIO); 667 } else if (r == ELOCKED) { 668 printf("VFS: ELOCKED talking to %d\n", driver_e); 669 return(EIO); 670 } 671 panic("block_io: can't send/receive: %d", r); 672 } 673 674 return(OK); 675 } 676 677 678 /*===========================================================================* 679 * bdev_up * 680 *===========================================================================*/ 681 void bdev_up(devmajor_t maj) 682 { 683 /* A new block device driver has been mapped in. This may affect both mounted 684 * file systems and open block-special files. 685 */ 686 int r, found, bits; 687 struct filp *rfilp; 688 struct vmnt *vmp; 689 struct vnode *vp; 690 char *label; 691 692 if (maj < 0 || maj >= NR_DEVICES) panic("VFS: out-of-bound major"); 693 label = dmap[maj].dmap_label; 694 found = 0; 695 696 /* For each block-special file that was previously opened on the affected 697 * device, we need to reopen it on the new driver. 698 */ 699 for (rfilp = filp; rfilp < &filp[NR_FILPS]; rfilp++) { 700 if (rfilp->filp_count < 1 || !(vp = rfilp->filp_vno)) continue; 701 if (major(vp->v_sdev) != maj) continue; 702 if (!S_ISBLK(vp->v_mode)) continue; 703 704 /* Reopen the device on the driver, once per filp. */ 705 bits = rfilp->filp_mode & (R_BIT|W_BIT); 706 if ((r = bdev_open(vp->v_sdev, bits)) != OK) { 707 printf("VFS: mounted dev %d/%d re-open failed: %d.\n", 708 maj, minor(vp->v_sdev), r); 709 dmap[maj].dmap_recovering = 0; 710 return; /* Give up entirely */ 711 } 712 713 found = 1; 714 } 715 716 /* Tell each affected mounted file system about the new endpoint. 717 */ 718 for (vmp = &vmnt[0]; vmp < &vmnt[NR_MNTS]; ++vmp) { 719 if (major(vmp->m_dev) != maj) continue; 720 721 /* Send the driver label to the mounted file system. */ 722 if (OK != req_newdriver(vmp->m_fs_e, vmp->m_dev, label)) 723 printf("VFS dev_up: error sending new driver label to %d\n", 724 vmp->m_fs_e); 725 } 726 727 /* If any block-special file was open for this major at all, also inform the 728 * root file system about the new driver. We do this even if the 729 * block-special file is linked to another mounted file system, merely 730 * because it is more work to check for that case. 731 */ 732 if (found) { 733 if (OK != req_newdriver(ROOT_FS_E, makedev(maj, 0), label)) 734 printf("VFSdev_up: error sending new driver label to %d\n", 735 ROOT_FS_E); 736 } 737 } 738 739 740 /*===========================================================================* 741 * cdev_generic_reply * 742 *===========================================================================*/ 743 static void cdev_generic_reply(message *m_ptr) 744 { 745 /* A character driver has results for an open, close, read, write, or ioctl 746 * call (i.e., everything except select). There may be a thread waiting for 747 * these results as part of an ongoing open, close, or (for read/write/ioctl) 748 * cancel call. If so, wake up that thread; if not, send a reply to the 749 * requesting process. This function MUST NOT block its calling thread. 750 */ 751 struct fproc *rfp; 752 struct worker_thread *wp; 753 endpoint_t proc_e; 754 int r, slot; 755 756 proc_e = m_ptr->m_lchardriver_vfs_reply.id; 757 758 if (m_ptr->m_lchardriver_vfs_reply.status == SUSPEND) { 759 printf("VFS: got SUSPEND from %d, not reviving\n", m_ptr->m_source); 760 return; 761 } 762 763 if (isokendpt(proc_e, &slot) != OK) { 764 printf("VFS: proc %d from %d not found\n", proc_e, m_ptr->m_source); 765 return; 766 } 767 rfp = &fproc[slot]; 768 wp = rfp->fp_worker; 769 if (wp != NULL && wp->w_task == who_e) { 770 assert(!fp_is_blocked(rfp)); 771 *wp->w_drv_sendrec = *m_ptr; 772 worker_signal(wp); /* Continue open/close/cancel */ 773 } else if (rfp->fp_blocked_on != FP_BLOCKED_ON_OTHER || 774 rfp->fp_task != m_ptr->m_source) { 775 /* This would typically be caused by a protocol error, i.e. a driver 776 * not properly following the character driver protocol rules. 777 */ 778 printf("VFS: proc %d not blocked on %d\n", proc_e, m_ptr->m_source); 779 } else { 780 /* Some services (inet) use the same infrastructure for nonblocking 781 * and cancelled requests, resulting in one of EINTR or EAGAIN when the 782 * other is really the appropriate code. Thus, cdev_cancel converts 783 * EAGAIN into EINTR, and we convert EINTR into EAGAIN here. 784 */ 785 r = m_ptr->m_lchardriver_vfs_reply.status; 786 revive(proc_e, (r == EINTR) ? EAGAIN : r); 787 } 788 } 789 790 791 /*===========================================================================* 792 * cdev_reply * 793 *===========================================================================*/ 794 void cdev_reply(void) 795 { 796 /* A character driver has results for us. */ 797 798 if (get_dmap(who_e) == NULL) { 799 printf("VFS: ignoring char dev reply from unknown driver %d\n", who_e); 800 return; 801 } 802 803 switch (call_nr) { 804 case CDEV_REPLY: 805 cdev_generic_reply(&m_in); 806 break; 807 case CDEV_SEL1_REPLY: 808 select_reply1(m_in.m_source, m_in.m_lchardriver_vfs_sel1.minor, 809 m_in.m_lchardriver_vfs_sel1.status); 810 break; 811 case CDEV_SEL2_REPLY: 812 select_reply2(m_in.m_source, m_in.m_lchardriver_vfs_sel2.minor, 813 m_in.m_lchardriver_vfs_sel2.status); 814 break; 815 default: 816 printf("VFS: char driver %u sent unknown reply %x\n", who_e, call_nr); 817 } 818 } 819 820 821 /*===========================================================================* 822 * bdev_reply * 823 *===========================================================================*/ 824 void bdev_reply(void) 825 { 826 /* A block driver has results for a call. There must be a thread waiting for 827 * these results - wake it up. This function MUST NOT block its calling thread. 828 */ 829 struct worker_thread *wp; 830 struct dmap *dp; 831 832 if ((dp = get_dmap(who_e)) == NULL) { 833 printf("VFS: ignoring block dev reply from unknown driver %d\n", 834 who_e); 835 return; 836 } 837 838 if (dp->dmap_servicing == INVALID_THREAD) { 839 printf("VFS: ignoring spurious block dev reply from %d\n", who_e); 840 return; 841 } 842 843 wp = worker_get(dp->dmap_servicing); 844 if (wp == NULL || wp->w_task != who_e) { 845 printf("VFS: no worker thread waiting for a reply from %d\n", who_e); 846 return; 847 } 848 849 assert(wp->w_drv_sendrec != NULL); 850 *wp->w_drv_sendrec = m_in; 851 wp->w_drv_sendrec = NULL; 852 worker_signal(wp); 853 } 854