Fix a number of core kernel issues related to HAMMER operation.
[games.git] / sys / kern / vfs_subr.c
index 8d691f5..30249bf 100644 (file)
@@ -37,7 +37,7 @@
  *
  *     @(#)vfs_subr.c  8.31 (Berkeley) 5/26/95
  * $FreeBSD: src/sys/kern/vfs_subr.c,v 1.249.2.30 2003/04/04 20:35:57 tegge Exp $
- * $DragonFly: src/sys/kern/vfs_subr.c,v 1.99 2006/09/19 11:47:36 corecode Exp $
+ * $DragonFly: src/sys/kern/vfs_subr.c,v 1.114 2008/05/18 05:54:25 dillon Exp $
  */
 
 /*
@@ -83,6 +83,7 @@
 
 #include <sys/buf2.h>
 #include <sys/thread2.h>
+#include <sys/sysref2.h>
 
 static MALLOC_DEFINE(M_NETADDR, "Export Host", "Export host address structure");
 
@@ -127,7 +128,7 @@ SYSCTL_INT(_kern, KERN_MAXVNODES, maxvnodes, CTLFLAG_RW,
 static void    vfs_free_addrlist (struct netexport *nep);
 static int     vfs_free_netcred (struct radix_node *rn, void *w);
 static int     vfs_hang_addrlist (struct mount *mp, struct netexport *nep,
-                                      struct export_args *argp);
+                                      const struct export_args *argp);
 
 extern int dev_ref_debug;
 
@@ -152,9 +153,9 @@ rb_buf_compare(struct buf *b1, struct buf *b2)
  * Returns non-zero if the vnode is a candidate for lazy msyncing.
  */
 static __inline int
-vshouldmsync(struct vnode *vp, int usecount)
+vshouldmsync(struct vnode *vp)
 {
-       if (vp->v_holdcnt != 0 || vp->v_usecount != usecount)
+       if (vp->v_auxrefs != 0 || vp->v_sysref.refcnt > 0)
                return (0);             /* other holders */
        if (vp->v_object &&
            (vp->v_object->ref_count || vp->v_object->resident_page_count)) {
@@ -179,8 +180,8 @@ vfs_subr_init(void)
         */
        /* desiredvnodes = maxproc + vmstats.v_page_count / 4; */
        desiredvnodes =
-               min(maxproc + vmstats.v_page_count /4,
-                   2 * (VM_MAX_KERNEL_ADDRESS - VM_MIN_KERNEL_ADDRESS) /
+               min(maxproc + vmstats.v_page_count / 4,
+                   2 * KvaSize /
                    (5 * (sizeof(struct vm_object) + sizeof(struct vnode))));
 
        lwkt_token_init(&spechash_token);
@@ -243,7 +244,8 @@ vattr_null(struct vattr *vap)
        vap->va_fsid = VNOVAL;
        vap->va_fileid = VNOVAL;
        vap->va_blocksize = VNOVAL;
-       vap->va_rdev = VNOVAL;
+       vap->va_rmajor = VNOVAL;
+       vap->va_rminor = VNOVAL;
        vap->va_atime.tv_sec = VNOVAL;
        vap->va_atime.tv_nsec = VNOVAL;
        vap->va_mtime.tv_sec = VNOVAL;
@@ -254,6 +256,7 @@ vattr_null(struct vattr *vap)
        vap->va_gen = VNOVAL;
        vap->va_vaflags = 0;
        vap->va_fsmid = VNOVAL;
+       /* va_*_uuid fields are only valid if related flags are set */
 }
 
 /*
@@ -390,6 +393,9 @@ vinvalbuf_bp(struct buf *bp, void *data)
         * check for it.  Note that vfs_bio_awrite expects
         * buffers to reside on a queue, while bwrite() and
         * brelse() do not.
+        *
+        * NOTE:  NO B_LOCKED CHECK.  Also no buf_checkwrite()
+        * check.  This code will write out the buffer, period.
         */
        if (((bp->b_flags & (B_DELWRI | B_INVAL)) == B_DELWRI) &&
            (info->flags & V_SAVE)) {
@@ -498,7 +504,7 @@ vtruncbuf(struct vnode *vp, off_t length, int blksize)
                vp->v_track_write.bk_waitflag = 1;
                tsleep(&vp->v_track_write, 0, "vbtrunc", 0);
                if (length == 0) {
-                       printf("Warning: vtruncbuf(): Had to wait for "
+                       kprintf("Warning: vtruncbuf(): Had to wait for "
                               "%d buffer I/Os to finish in %s\n",
                               count, filename);
                }
@@ -517,7 +523,7 @@ vtruncbuf(struct vnode *vp, off_t length, int blksize)
                                vtruncbuf_bp_trunc_cmp,
                                vtruncbuf_bp_trunc, &truncloffset);
                if (count) {
-                       printf("Warning: vtruncbuf():  Had to re-clean %d "
+                       kprintf("Warning: vtruncbuf():  Had to re-clean %d "
                               "left over buffers in %s\n", count, filename);
                }
        } while(count);
@@ -692,7 +698,7 @@ vfsync(struct vnode *vp, int waitfor, int passes,
                                vfsync_bp, &info);
                        error = vfsync_wait_output(vp, waitoutput);
                        if (info.skippedbufs)
-                               printf("Warning: vfsync skipped %d dirty bufs in pass2!\n", info.skippedbufs);
+                               kprintf("Warning: vfsync skipped %d dirty bufs in pass2!\n", info.skippedbufs);
                }
                while (error == 0 && passes > 0 &&
                    !RB_EMPTY(&vp->v_rbdirty_tree)) {
@@ -771,7 +777,7 @@ vfsync_bp(struct buf *bp, void *data)
         * Ignore buffers that we cannot immediately lock.  XXX
         */
        if (BUF_LOCK(bp, LK_EXCLUSIVE | LK_NOWAIT)) {
-               printf("Warning: vfsync_bp skipping dirty buffer %p\n", bp);
+               kprintf("Warning: vfsync_bp skipping dirty buffer %p\n", bp);
                ++info->skippedbufs;
                return(0);
        }
@@ -791,6 +797,15 @@ vfsync_bp(struct buf *bp, void *data)
                return(0);
        }
 
+       /*
+        * Ask bioops if it is ok to sync 
+        */
+       if (LIST_FIRST(&bp->b_dep) != NULL && buf_checkwrite(bp)) {
+               bremfree(bp);
+               brelse(bp);
+               return(0);
+       }
+
        if (info->synchronous) {
                /*
                 * Synchronous flushing.  An error may be returned.
@@ -973,7 +988,7 @@ bdevvp(cdev_t dev, struct vnode **vpp)
        struct vnode *nvp;
        int error;
 
-       if (dev == NOCDEV) {
+       if (dev == NULL) {
                *vpp = NULLVP;
                return (ENXIO);
        }
@@ -984,7 +999,8 @@ bdevvp(cdev_t dev, struct vnode **vpp)
        }
        vp = nvp;
        vp->v_type = VCHR;
-       vp->v_udev = dev->si_udev;
+       vp->v_umajor = dev->si_umajor;
+       vp->v_uminor = dev->si_uminor;
        vx_unlock(vp);
        *vpp = vp;
        return (0);
@@ -995,13 +1011,13 @@ v_associate_rdev(struct vnode *vp, cdev_t dev)
 {
        lwkt_tokref ilock;
 
-       if (dev == NULL || dev == NOCDEV)
+       if (dev == NULL)
                return(ENXIO);
        if (dev_is_good(dev) == 0)
                return(ENXIO);
        KKASSERT(vp->v_rdev == NULL);
        if (dev_ref_debug)
-               printf("Z1");
+               kprintf("Z1");
        vp->v_rdev = reference_dev(dev);
        lwkt_gettoken(&ilock, &spechash_token);
        SLIST_INSERT_HEAD(&dev->si_hlist, vp, v_cdevnext);
@@ -1031,24 +1047,24 @@ v_release_rdev(struct vnode *vp)
  * disassociated on last close.
  */
 void
-addaliasu(struct vnode *nvp, udev_t nvp_udev)
+addaliasu(struct vnode *nvp, int x, int y)
 {
        if (nvp->v_type != VBLK && nvp->v_type != VCHR)
                panic("addaliasu on non-special vnode");
-       nvp->v_udev = nvp_udev;
+       nvp->v_umajor = x;
+       nvp->v_uminor = y;
 }
 
 /*
  * Disassociate a vnode from its underlying filesystem. 
  *
- * The vnode must be VX locked, referenced, and v_spinlock must be held.
- * This routine releases v_spinlock.
- *
- * If there are v_usecount references to the vnode other then ours we have
- * to VOP_CLOSE the vnode before we can deactivate and reclaim it.
+ * The vnode must be VX locked and referenced.  In all normal situations
+ * there are no active references.  If vclean_vxlocked() is called while
+ * there are active references, the vnode is being ripped out and we have
+ * to call VOP_CLOSE() as appropriate before we can reclaim it.
  */
 void
-vclean_interlocked(struct vnode *vp, int flags)
+vclean_vxlocked(struct vnode *vp, int flags)
 {
        int active;
        int n;
@@ -1056,20 +1072,16 @@ vclean_interlocked(struct vnode *vp, int flags)
 
        /*
         * If the vnode has already been reclaimed we have nothing to do.
-        * VRECLAIMED must be interlocked with the vnode's spinlock.
         */
-       if (vp->v_flag & VRECLAIMED) {
-               spin_unlock_wr(&vp->v_spinlock);
+       if (vp->v_flag & VRECLAIMED)
                return;
-       }
        vp->v_flag |= VRECLAIMED;
-       spin_unlock_wr(&vp->v_spinlock);
 
        /*
         * Scrap the vfs cache
         */
        while (cache_inval_vp(vp, 0) != 0) {
-               printf("Warning: vnode %p clean/cache_resolution race detected\n", vp);
+               kprintf("Warning: vnode %p clean/cache_resolution race detected\n", vp);
                tsleep(vp, 0, "vclninv", 2);
        }
 
@@ -1078,7 +1090,7 @@ vclean_interlocked(struct vnode *vp, int flags)
         * before we clean it out so that its count cannot fall to zero and
         * generate a race against ourselves to recycle it.
         */
-       active = (vp->v_usecount > 1);
+       active = sysref_isactive(&vp->v_sysref);
 
        /*
         * Clean out any buffers associated with the vnode and destroy its
@@ -1101,7 +1113,7 @@ vclean_interlocked(struct vnode *vp, int flags)
                        else
                                VOP_CLOSE(vp, FNONBLOCK);
                        if (vp->v_opencount == n) {
-                               printf("Warning: unable to force-close"
+                               kprintf("Warning: unable to force-close"
                                       " vnode %p\n", vp);
                                break;
                        }
@@ -1109,7 +1121,7 @@ vclean_interlocked(struct vnode *vp, int flags)
        }
 
        /*
-        * If the vnode has not be deactivated, deactivated it.  Deactivation
+        * If the vnode has not been deactivated, deactivated it.  Deactivation
         * can create new buffers and VM pages so we have to call vinvalbuf()
         * again to make sure they all get flushed.
         *
@@ -1136,7 +1148,6 @@ vclean_interlocked(struct vnode *vp, int flags)
        }
        KKASSERT((vp->v_flag & VOBJBUF) == 0);
 
-
        /*
         * Reclaim the vnode.
         */
@@ -1149,6 +1160,16 @@ vclean_interlocked(struct vnode *vp, int flags)
        vp->v_ops = &dead_vnode_vops_p;
        vn_pollgone(vp);
        vp->v_tag = VT_NON;
+
+       /*
+        * If we are destroying an active vnode, reactivate it now that
+        * we have reassociated it with deadfs.  This prevents the system
+        * from crashing on the vnode due to it being unexpectedly marked
+        * as inactive or reclaimed.
+        */
+       if (active && (flags & DOCLOSE)) {
+               vp->v_flag &= ~(VINACTIVE|VRECLAIMED);
+       }
 }
 
 /*
@@ -1184,10 +1205,10 @@ vop_stdrevoke(struct vop_revoke_args *ap)
         * The passed vp will probably show up in the list, do not VX lock
         * it twice!
         */
-       if (vp->v_type != VCHR && vp->v_type != VBLK)
+       if (vp->v_type != VCHR)
                return(0);
        if ((dev = vp->v_rdev) == NULL) {
-               if ((dev = udev2dev(vp->v_udev, vp->v_type == VBLK)) == NOCDEV)
+               if ((dev = get_dev(vp->v_umajor, vp->v_uminor)) == NULL)
                        return(0);
        }
        reference_dev(dev);
@@ -1196,7 +1217,7 @@ vop_stdrevoke(struct vop_revoke_args *ap)
                if (vp != vq)
                        vx_get(vq);
                if (vq == SLIST_FIRST(&dev->si_hlist))
-                       vgone(vq);
+                       vgone_vxlocked(vq);
                if (vp != vq)
                        vx_put(vq);
        }
@@ -1206,21 +1227,41 @@ vop_stdrevoke(struct vop_revoke_args *ap)
 }
 
 /*
- * Recycle an unused vnode to the front of the free list.
+ * This is called when the object underlying a vnode is being destroyed,
+ * such as in a remove().  Try to recycle the vnode immediately if the
+ * only active reference is our reference.
  *
- * Returns 1 if we were successfully able to recycle the vnode, 
- * 0 otherwise.
+ * Directory vnodes in the namecache with children cannot be immediately
+ * recycled because numerous VOP_N*() ops require them to be stable.
  */
 int
 vrecycle(struct vnode *vp)
 {
-       if (vp->v_usecount == 1) {
-               vgone(vp);
+       if (vp->v_sysref.refcnt <= 1) {
+               if (cache_inval_vp_nonblock(vp))
+                       return(0);
+               vgone_vxlocked(vp);
                return (1);
        }
        return (0);
 }
 
+/*
+ * Return the maximum I/O size allowed for strategy calls on VP.
+ *
+ * If vp is VCHR or VBLK we dive the device, otherwise we use
+ * the vp's mount info.
+ */
+int
+vmaxiosize(struct vnode *vp)
+{
+       if (vp->v_type == VBLK || vp->v_type == VCHR) {
+               return(vp->v_rdev->si_iosize_max);
+       } else {
+               return(vp->v_mount->mnt_iosize_max);
+       }
+}
+
 /*
  * Eliminate all activity associated with a vnode in preparation for reuse.
  *
@@ -1238,19 +1279,13 @@ vrecycle(struct vnode *vp)
  * Instead, it happens automatically when the caller releases the VX lock
  * (assuming there aren't any other references).
  */
-void
-vgone(struct vnode *vp)
-{
-       spin_lock_wr(&vp->v_spinlock);
-       vgone_interlocked(vp);
-}
 
 void
-vgone_interlocked(struct vnode *vp)
+vgone_vxlocked(struct vnode *vp)
 {
        /*
         * assert that the VX lock is held.  This is an absolute requirement
-        * now for vgone() to be called.
+        * now for vgone_vxlocked() to be called.
         */
        KKASSERT(vp->v_lock.lk_exclusivecount == 1);
 
@@ -1258,8 +1293,7 @@ vgone_interlocked(struct vnode *vp)
         * Clean out the filesystem specific data and set the VRECLAIMED
         * bit.  Also deactivate the vnode if necessary. 
         */
-       vclean_interlocked(vp, DOCLOSE);
-       /* spinlock unlocked */
+       vclean_vxlocked(vp, DOCLOSE);
 
        /*
         * Delete from old mount point vnode list, if on one.
@@ -1307,7 +1341,7 @@ vfinddev(cdev_t dev, enum vtype type, struct vnode **vpp)
 /*
  * Calculate the total number of references to a special device.  This
  * routine may only be called for VBLK and VCHR vnodes since v_rdev is
- * an overloaded field.  Since udev2dev can now return NOCDEV, we have
+ * an overloaded field.  Since udev2dev can now return NULL, we have
  * to check for a NULL v_rdev.
  */
 int
@@ -1320,7 +1354,8 @@ count_dev(cdev_t dev)
        if (SLIST_FIRST(&dev->si_hlist)) {
                lwkt_gettoken(&ilock, &spechash_token);
                SLIST_FOREACH(vp, &dev->si_hlist, v_cdevnext) {
-                       count += vp->v_usecount;
+                       if (vp->v_sysref.refcnt > 0)
+                               count += vp->v_sysref.refcnt;
                }
                lwkt_reltoken(&ilock);
        }
@@ -1328,11 +1363,11 @@ count_dev(cdev_t dev)
 }
 
 int
-count_udev(udev_t udev)
+count_udev(int x, int y)
 {
        cdev_t dev;
 
-       if ((dev = udev2dev(udev, 0)) == NOCDEV)
+       if ((dev = get_dev(x, y)) == NULL)
                return(0);
        return(count_dev(dev));
 }
@@ -1364,11 +1399,11 @@ retry:
                 * that the object is associated with the vp.
                 */
                object->ref_count--;
-               vp->v_usecount--;
+               vrele(vp);
        } else {
                if (object->flags & OBJ_DEAD) {
                        vn_unlock(vp);
-                       tsleep(object, 0, "vodead", 0);
+                       vm_object_dead_sleep(object, "vodead");
                        vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
                        goto retry;
                }
@@ -1391,12 +1426,12 @@ vprint(char *label, struct vnode *vp)
        char buf[96];
 
        if (label != NULL)
-               printf("%s: %p: ", label, (void *)vp);
+               kprintf("%s: %p: ", label, (void *)vp);
        else
-               printf("%p: ", (void *)vp);
-       printf("type %s, usecount %d, writecount %d, refcount %d,",
-           typename[vp->v_type], vp->v_usecount, vp->v_writecount,
-           vp->v_holdcnt);
+               kprintf("%p: ", (void *)vp);
+       kprintf("type %s, sysrefs %d, writecount %d, holdcnt %d,",
+               typename[vp->v_type],
+               vp->v_sysref.refcnt, vp->v_writecount, vp->v_auxrefs);
        buf[0] = '\0';
        if (vp->v_flag & VROOT)
                strcat(buf, "|VROOT");
@@ -1409,11 +1444,11 @@ vprint(char *label, struct vnode *vp)
        if (vp->v_flag & VOBJBUF)
                strcat(buf, "|VOBJBUF");
        if (buf[0] != '\0')
-               printf(" flags (%s)", &buf[1]);
+               kprintf(" flags (%s)", &buf[1]);
        if (vp->v_data == NULL) {
-               printf("\n");
+               kprintf("\n");
        } else {
-               printf("\n\t");
+               kprintf("\n\t");
                VOP_PRINT(vp);
        }
 }
@@ -1429,7 +1464,7 @@ static int db_show_locked_vnodes(struct mount *mp, void *data);
  */
 DB_SHOW_COMMAND(lockedvnodes, lockedvnodes)
 {
-       printf("Locked vnodes\n");
+       kprintf("Locked vnodes\n");
        mountlist_scan(db_show_locked_vnodes, NULL, 
                        MNTSCAN_FORWARD|MNTSCAN_NOBUSY);
 }
@@ -1533,9 +1568,11 @@ vfs_mountedon(struct vnode *vp)
 {
        cdev_t dev;
 
-       if ((dev = vp->v_rdev) == NULL)
-               dev = udev2dev(vp->v_udev, (vp->v_type == VBLK));
-       if (dev != NOCDEV && dev->si_mountpoint)
+       if ((dev = vp->v_rdev) == NULL) {
+               if (vp->v_type != VBLK)
+                       dev = get_dev(vp->v_uminor, vp->v_umajor);
+       }
+       if (dev != NULL && dev->si_mountpoint)
                return (EBUSY);
        return (0);
 }
@@ -1567,12 +1604,12 @@ vfs_umountall_callback(struct mount *mp, void *data)
        error = dounmount(mp, MNT_FORCE);
        if (error) {
                mountlist_remove(mp);
-               printf("unmount of filesystem mounted from %s failed (", 
+               kprintf("unmount of filesystem mounted from %s failed (", 
                        mp->mnt_stat.f_mntfromname);
                if (error == EBUSY)
-                       printf("BUSY)\n");
+                       kprintf("BUSY)\n");
                else
-                       printf("%d)\n", error);
+                       kprintf("%d)\n", error);
        }
        return(1);
 }
@@ -1583,7 +1620,7 @@ vfs_umountall_callback(struct mount *mp, void *data)
  */
 static int
 vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
-               struct export_args *argp)
+               const struct export_args *argp)
 {
        struct netcred *np;
        struct radix_node_head *rnh;
@@ -1610,8 +1647,7 @@ vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
                return (EINVAL);
 
        i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen;
-       np = (struct netcred *) kmalloc(i, M_NETADDR, M_WAITOK);
-       bzero((caddr_t) np, i);
+       np = (struct netcred *) kmalloc(i, M_NETADDR, M_WAITOK | M_ZERO);
        saddr = (struct sockaddr *) (np + 1);
        if ((error = copyin(argp->ex_addr, (caddr_t) saddr, argp->ex_addrlen)))
                goto out;
@@ -1687,7 +1723,8 @@ vfs_free_addrlist(struct netexport *nep)
 }
 
 int
-vfs_export(struct mount *mp, struct netexport *nep, struct export_args *argp)
+vfs_export(struct mount *mp, struct netexport *nep,
+          const struct export_args *argp)
 {
        int error;
 
@@ -1719,7 +1756,7 @@ vfs_export(struct mount *mp, struct netexport *nep, struct export_args *argp)
  */
 int
 vfs_setpublicfs(struct mount *mp, struct netexport *nep,
-               struct export_args *argp)
+               const struct export_args *argp)
 {
        int error;
        struct vnode *rvp;
@@ -1867,7 +1904,7 @@ vfs_msync_scan1(struct mount *mp, struct vnode *vp, void *data)
        int flags = (int)data;
 
        if ((vp->v_flag & VRECLAIMED) == 0) {
-               if (vshouldmsync(vp, 0))
+               if (vshouldmsync(vp))
                        return(0);      /* call scan2 */
                if ((mp->mnt_flag & MNT_RDONLY) == 0 &&
                    (vp->v_flag & VOBJDIRTY) &&
@@ -1997,7 +2034,7 @@ cdev_t
 vn_todev(struct vnode *vp)
 {
        if (vp->v_type != VBLK && vp->v_type != VCHR)
-               return (NOCDEV);
+               return (NULL);
        KKASSERT(vp->v_rdev != NULL);
        return (vp->v_rdev);
 }
@@ -2011,15 +2048,16 @@ vn_isdisk(struct vnode *vp, int *errp)
 {
        cdev_t dev;
 
-       if (vp->v_type != VBLK && vp->v_type != VCHR) {
+       if (vp->v_type != VCHR) {
                if (errp != NULL)
                        *errp = ENOTBLK;
                return (0);
        }
 
        if ((dev = vp->v_rdev) == NULL)
-               dev = udev2dev(vp->v_udev, (vp->v_type == VBLK));
-       if (dev == NULL || dev == NOCDEV) {
+               dev = get_dev(vp->v_umajor, vp->v_uminor);
+
+       if (dev == NULL) {
                if (errp != NULL)
                        *errp = ENXIO;
                return (0);