Device layer rollup commit.
[dragonfly.git] / sys / vfs / specfs / spec_vnops.c
index e27ae0d..dec787c 100644 (file)
@@ -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 <sys/param.h>
@@ -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;
-               <other random data follows, presumably>
-       } */ *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.
         */