X-Git-Url: https://gitweb.dragonflybsd.org/dragonfly.git/blobdiff_plain/4faddc0132d70ccaa104372c58830ef4d1304bd2..e4c9c0c82658e0eca11c3e516c41021ba48b0093:/sys/vfs/specfs/spec_vnops.c diff --git a/sys/vfs/specfs/spec_vnops.c b/sys/vfs/specfs/spec_vnops.c index e27ae0d32a..dec787c8e7 100644 --- a/sys/vfs/specfs/spec_vnops.c +++ b/sys/vfs/specfs/spec_vnops.c @@ -32,7 +32,7 @@ * * @(#)spec_vnops.c 8.14 (Berkeley) 5/21/95 * $FreeBSD: src/sys/miscfs/specfs/spec_vnops.c,v 1.131.2.4 2001/02/26 04:23:20 jlemon Exp $ - * $DragonFly: src/sys/vfs/specfs/spec_vnops.c,v 1.13 2004/03/01 06:33:23 dillon Exp $ + * $DragonFly: src/sys/vfs/specfs/spec_vnops.c,v 1.15 2004/05/19 22:53:06 dillon Exp $ */ #include @@ -113,12 +113,13 @@ static struct vnodeopv_desc spec_vnodeop_opv_desc = VNODEOP_SET(spec_vnodeop_opv_desc); +extern int dev_ref_debug; + +/* + * spec_vnoperate(struct vnodeop_desc *a_desc, ...) + */ int -spec_vnoperate(ap) - struct vop_generic_args /* { - struct vnodeop_desc *a_desc; - - } */ *ap; +spec_vnoperate(struct vop_generic_args *ap) { return (VOCALL(spec_vnodeop_p, ap->a_desc->vdesc_offset, ap)); } @@ -127,20 +128,18 @@ static void spec_getpages_iodone (struct buf *bp); /* * Open a special file. + * + * spec_open(struct vnode *a_vp, int a_mode, struct ucred *a_cred, + * struct thread *a_td) */ /* ARGSUSED */ static int -spec_open(ap) - struct vop_open_args /* { - struct vnode *a_vp; - int a_mode; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; +spec_open(struct vop_open_args *ap) { struct vnode *vp = ap->a_vp; - dev_t dev = vp->v_rdev; + dev_t dev; int error; + int isblk = (vp->v_type == VBLK) ? 1 : 0; const char *cp; /* @@ -149,10 +148,40 @@ spec_open(ap) if (vp->v_mount && (vp->v_mount->mnt_flag & MNT_NODEV)) return (ENXIO); - if (dev_dport(dev) == NULL) - return ENXIO; + /* + * Resolve the device. If the vnode is already open v_rdev may + * already be resolved. However, if the device changes out from + * under us we report it (and, for now, we allow it). + */ + if (vp->v_rdev != NULL) { + dev = udev2dev(vp->v_udev, isblk); + if (dev != vp->v_rdev) { + printf( + "Warning: spec_open: dev %s was lost", + vp->v_rdev->si_name); + v_release_rdev(vp); + error = v_associate_rdev(vp, + udev2dev(vp->v_udev, isblk)); + if (error) + printf(", reacquisition failed\n"); + else + printf(", reacquisition successful\n"); + } else { + error = 0; + } + } else { + error = v_associate_rdev(vp, udev2dev(vp->v_udev, isblk)); + } + if (error) + return(error); + + dev = vp->v_rdev; - /* Make this field valid before any I/O in ->d_open */ + /* + * Make this field valid before any I/O in ->d_open. XXX the + * device itself should probably be required to initialize + * this field in d_open. + */ if (!dev->si_iosize_max) dev->si_iosize_max = DFLTPHYS; @@ -167,35 +196,45 @@ spec_open(ap) /* * Never allow opens for write if the device is mounted R/W */ - if (vp->v_specmountpoint != NULL && - !(vp->v_specmountpoint->mnt_flag & MNT_RDONLY)) - return (EBUSY); + if (vp->v_rdev && vp->v_rdev->si_mountpoint && + !(vp->v_rdev->si_mountpoint->mnt_flag & MNT_RDONLY)) { + error = EBUSY; + goto done; + } /* * When running in secure mode, do not allow opens * for writing if the device is mounted */ - if (securelevel >= 1 && vp->v_specmountpoint != NULL) - return (EPERM); + if (securelevel >= 1 && vfs_mountedon(vp)) { + error = EPERM; + goto done; + } /* * When running in very secure mode, do not allow * opens for writing of any devices. */ - if (securelevel >= 2) - return (EPERM); + if (securelevel >= 2) { + error = EPERM; + goto done; + } } /* XXX: Special casing of ttys for deadfs. Probably redundant */ if (dev_dflags(dev) & D_TTY) vp->v_flag |= VISTTY; + /* + * dev_dopen() is always called for each open. dev_dclose() is + * only called for the last close unless D_TRACKCLOSE is set. + */ VOP_UNLOCK(vp, NULL, 0, ap->a_td); error = dev_dopen(dev, ap->a_mode, S_IFCHR, ap->a_td); vn_lock(vp, NULL, LK_EXCLUSIVE | LK_RETRY, ap->a_td); if (error) - return (error); + goto done; if (dev_dflags(dev) & D_TTY) { if (dev->si_tty) { @@ -219,21 +258,26 @@ spec_open(ap) dev_dname(dev), cp); } } + ++vp->v_opencount; + if (dev_ref_debug) + printf("spec_open: %s %d\n", dev->si_name, vp->v_opencount); +done: + if (error) { + if (vp->v_opencount == 0) + v_release_rdev(vp); + } return (error); } /* * Vnode op for read + * + * spec_read(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, + * struct ucred *a_cred) */ /* ARGSUSED */ static int -spec_read(ap) - struct vop_read_args /* { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ *ap; +spec_read(struct vop_read_args *ap) { struct vnode *vp; struct thread *td; @@ -257,16 +301,13 @@ spec_read(ap) /* * Vnode op for write + * + * spec_write(struct vnode *a_vp, struct uio *a_uio, int a_ioflag, + * struct ucred *a_cred) */ /* ARGSUSED */ static int -spec_write(ap) - struct vop_write_args /* { - struct vnode *a_vp; - struct uio *a_uio; - int a_ioflag; - struct ucred *a_cred; - } */ *ap; +spec_write(struct vop_write_args *ap) { struct vnode *vp; struct thread *td; @@ -287,18 +328,13 @@ spec_write(ap) /* * Device ioctl operation. + * + * spec_ioctl(struct vnode *a_vp, int a_command, caddr_t a_data, + * int a_fflag, struct ucred *a_cred, struct thread *a_td) */ /* ARGSUSED */ static int -spec_ioctl(ap) - struct vop_ioctl_args /* { - struct vnode *a_vp; - int a_command; - caddr_t a_data; - int a_fflag; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; +spec_ioctl(struct vop_ioctl_args *ap) { dev_t dev; @@ -307,15 +343,13 @@ spec_ioctl(ap) ap->a_fflag, ap->a_td)); } +/* + * spec_poll(struct vnode *a_vp, int a_events, struct ucred *a_cred, + * struct thread *a_td) + */ /* ARGSUSED */ static int -spec_poll(ap) - struct vop_poll_args /* { - struct vnode *a_vp; - int a_events; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; +spec_poll(struct vop_poll_args *ap) { dev_t dev; @@ -323,13 +357,12 @@ spec_poll(ap) return (dev_dpoll(dev, ap->a_events, ap->a_td)); } +/* + * spec_kqfilter(struct vnode *a_vp, struct knote *a_kn) + */ /* ARGSUSED */ static int -spec_kqfilter(ap) - struct vop_kqfilter_args /* { - struct vnode *a_vp; - struct knote *a_kn; - } */ *ap; +spec_kqfilter(struct vop_kqfilter_args *ap) { dev_t dev; @@ -339,16 +372,13 @@ spec_kqfilter(ap) /* * Synch buffers associated with a block device + * + * spec_fsync(struct vnode *a_vp, struct ucred *a_cred, + * int a_waitfor, struct thread *a_td) */ /* ARGSUSED */ static int -spec_fsync(ap) - struct vop_fsync_args /* { - struct vnode *a_vp; - struct ucred *a_cred; - int a_waitfor; - struct thread *a_td; - } */ *ap; +spec_fsync(struct vop_fsync_args *ap) { struct vnode *vp = ap->a_vp; struct buf *bp; @@ -419,27 +449,23 @@ loop2: return (0); } +/* + * spec_inactive(struct vnode *a_vp, struct thread *a_td) + */ static int -spec_inactive(ap) - struct vop_inactive_args /* { - struct vnode *a_vp; - struct thread *a_td; - } */ *ap; +spec_inactive(struct vop_inactive_args *ap) { - VOP_UNLOCK(ap->a_vp, NULL, 0, ap->a_td); return (0); } /* * Just call the device strategy routine + * + * spec_strategy(struct vnode *a_vp, struct buf *a_bp) */ static int -spec_strategy(ap) - struct vop_strategy_args /* { - struct vnode *a_vp; - struct buf *a_bp; - } */ *ap; +spec_strategy(struct vop_strategy_args *ap) { struct buf *bp; struct vnode *vp; @@ -455,7 +481,7 @@ spec_strategy(ap) * and write counts for disks that have associated filesystems. */ vp = ap->a_vp; - if (vn_isdisk(vp, NULL) && (mp = vp->v_specmountpoint) != NULL) { + if (vn_isdisk(vp, NULL) && (mp = vp->v_rdev->si_mountpoint) != NULL) { if ((bp->b_flags & B_READ) == 0) { if (bp->b_lock.lk_lockholder == LK_KERNTHREAD) mp->mnt_stat.f_asyncwrites++; @@ -468,25 +494,16 @@ spec_strategy(ap) mp->mnt_stat.f_syncreads++; } } -#if 0 - KASSERT(devsw(bp->b_dev) != NULL, - ("No devsw on dev %s responsible for buffer %p\n", - devtoname(bp->b_dev), bp)); - KASSERT(devsw(bp->b_dev)->d_strategy != NULL, - ("No strategy on dev %s responsible for buffer %p\n", - devtoname(bp->b_dev), bp)); -#endif + bp->b_dev = vp->v_rdev; BUF_STRATEGY(bp, 0); return (0); } +/* + * spec_freeblks(struct vnode *a_vp, daddr_t a_addr, daddr_t a_length) + */ static int -spec_freeblks(ap) - struct vop_freeblks_args /* { - struct vnode *a_vp; - daddr_t a_addr; - daddr_t a_length; - } */ *ap; +spec_freeblks(struct vop_freeblks_args *ap) { struct buf *bp; @@ -510,17 +527,12 @@ spec_freeblks(ap) * Implement degenerate case where the block requested is the block * returned, and assume that the entire device is contiguous in regards * to the contiguous block range (runp and runb). + * + * spec_bmap(struct vnode *a_vp, daddr_t a_bn, struct vnode **a_vpp, + * daddr_t *a_bnp, int *a_runp, int *a_runb) */ static int -spec_bmap(ap) - struct vop_bmap_args /* { - struct vnode *a_vp; - daddr_t a_bn; - struct vnode **a_vpp; - daddr_t *a_bnp; - int *a_runp; - int *a_runb; - } */ *ap; +spec_bmap(struct vop_bmap_args *ap) { struct vnode *vp = ap->a_vp; int runp = 0; @@ -541,20 +553,18 @@ spec_bmap(ap) /* * Device close routine + * + * spec_close(struct vnode *a_vp, int a_fflag, struct ucred *a_cred, + * struct thread *a_td) */ /* ARGSUSED */ static int -spec_close(ap) - struct vop_close_args /* { - struct vnode *a_vp; - int a_fflag; - struct ucred *a_cred; - struct thread *a_td; - } */ *ap; +spec_close(struct vop_close_args *ap) { struct proc *p = ap->a_td->td_proc; struct vnode *vp = ap->a_vp; dev_t dev = vp->v_rdev; + int error; /* * Hack: a tty device that is a controlling terminal @@ -565,74 +575,77 @@ spec_close(ap) * if the reference count is 2 (this last descriptor * plus the session), release the reference from the session. */ - if (vcount(vp) == 2 && p && (vp->v_flag & VXLOCK) == 0 && - vp == p->p_session->s_ttyvp) { + reference_dev(dev); + if (vcount(vp) == 2 && vp->v_opencount == 1 && + p && (vp->v_flag & VXLOCK) == 0 && vp == p->p_session->s_ttyvp) { vrele(vp); p->p_session->s_ttyvp = NULL; } + /* - * We do not want to really close the device if it - * is still in use unless we are trying to close it - * forcibly. Since every use (buffer, vnode, swap, cmap) - * holds a reference to the vnode, and because we mark - * any other vnodes that alias this device, when the - * sum of the reference counts on all the aliased - * vnodes descends to one, we are on last close. + * Vnodes can be opened and close multiple times. Do not really + * close the device unless (1) it is being closed forcibly, + * (2) the device wants to track closes, or (3) this is the last + * vnode doing its last close on the device. + * + * XXX the VXLOCK (force close) case can leave vnodes referencing + * a closed device. */ - if (vp->v_flag & VXLOCK) { - /* Forced close */ - } else if (dev_dflags(dev) & D_TRACKCLOSE) { - /* Keep device updated on status */ - } else if (vcount(vp) > 1) { - return (0); + if ((vp->v_flag & VXLOCK) || + (dev_dflags(dev) & D_TRACKCLOSE) || + (vcount(vp) <= 1 && vp->v_opencount == 1)) { + error = dev_dclose(dev, ap->a_fflag, S_IFCHR, ap->a_td); + } else { + error = 0; } - return (dev_dclose(dev, ap->a_fflag, S_IFCHR, ap->a_td)); + + /* + * Track the actual opens and closes on the vnode. The last close + * disassociates the rdev. + */ + KKASSERT(vp->v_opencount > 0); + if (dev_ref_debug) + printf("spec_close: %s %d\n", dev->si_name, vp->v_opencount - 1); + if (--vp->v_opencount == 0) + v_release_rdev(vp); + release_dev(dev); + return(error); } /* * Print out the contents of a special device vnode. + * + * spec_print(struct vnode *a_vp) */ static int -spec_print(ap) - struct vop_print_args /* { - struct vnode *a_vp; - } */ *ap; +spec_print(struct vop_print_args *ap) { - printf("tag VT_NON, dev %s\n", devtoname(ap->a_vp->v_rdev)); return (0); } /* * Special device advisory byte-level locks. + * + * spec_advlock(struct vnode *a_vp, caddr_t a_id, int a_op, + * struct flock *a_fl, int a_flags) */ /* ARGSUSED */ static int -spec_advlock(ap) - struct vop_advlock_args /* { - struct vnode *a_vp; - caddr_t a_id; - int a_op; - struct flock *a_fl; - int a_flags; - } */ *ap; +spec_advlock(struct vop_advlock_args *ap) { - return (ap->a_flags & F_FLOCK ? EOPNOTSUPP : EINVAL); } static void -spec_getpages_iodone(bp) - struct buf *bp; +spec_getpages_iodone(struct buf *bp) { - bp->b_flags |= B_DONE; wakeup(bp); } static int -spec_getpages(ap) - struct vop_getpages_args *ap; +spec_getpages(struct vop_getpages_args *ap) { vm_offset_t kva; int error; @@ -676,7 +689,7 @@ spec_getpages(ap) * the device. i.e. it's usually '/dev'. We need the physical block * size for the device itself. * - * We can't use v_specmountpoint because it only exists when the + * We can't use v_rdev->si_mountpoint because it only exists when the * block device is mounted. However, we can use v_rdev. */