No refcount problems, but no caching.
authorJohn Heidemann <heideman@ucbvax.Berkeley.EDU>
Sat, 11 Jul 1992 14:17:45 +0000 (06:17 -0800)
committerJohn Heidemann <heideman@ucbvax.Berkeley.EDU>
Sat, 11 Jul 1992 14:17:45 +0000 (06:17 -0800)
SCCS-vsn: sys/miscfs/nullfs/null.h 1.5
SCCS-vsn: sys/miscfs/nullfs/null_subr.c 1.5
SCCS-vsn: sys/miscfs/nullfs/null_vfsops.c 1.5
SCCS-vsn: sys/miscfs/nullfs/null_vnops.c 1.5

usr/src/sys/miscfs/nullfs/null.h
usr/src/sys/miscfs/nullfs/null_subr.c
usr/src/sys/miscfs/nullfs/null_vfsops.c
usr/src/sys/miscfs/nullfs/null_vnops.c

index 5517dcb..8606093 100644 (file)
@@ -37,6 +37,7 @@ extern int null_node_create __P((struct mount *mp, struct vnode *target, struct
 
 #define        MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data))
 #define        VTONULL(vp) ((struct null_node *)(vp)->v_data)
 
 #define        MOUNTTONULLMOUNT(mp) ((struct null_mount *)((mp)->mnt_data))
 #define        VTONULL(vp) ((struct null_node *)(vp)->v_data)
+#define        NULLTOV(xp) ((xp)->null_vnode)
 #ifdef NULLFS_DIAGNOSTIC
 extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno));
 #define        NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__)
 #ifdef NULLFS_DIAGNOSTIC
 extern struct vnode *null_checkvp __P((struct vnode *vp, char *fil, int lno));
 #define        NULLVPTOLOWERVP(vp) null_checkvp((vp), __FILE__, __LINE__)
index d5c9af4..2c3e20e 100644 (file)
 
 /*
  * Null layer cache:
 
 /*
  * Null layer cache:
- * Each cache entry holds a reference to the target vnode
+ * Each cache entry holds a reference to the lower vnode
  * along with a pointer to the alias vnode.  When an
  * along with a pointer to the alias vnode.  When an
- * entry is added the target vnode is VREF'd.  When the
- * alias is removed the target vnode is vrele'd.
+ * entry is added the lower vnode is VREF'd.  When the
+ * alias is removed the lower vnode is vrele'd.
  */
 
 /*
  */
 
 /*
@@ -60,43 +60,44 @@ nullfs_init()
 }
 
 /*
 }
 
 /*
- * Compute hash list for given target vnode
+ * Compute hash list for given lower vnode
  */
 static struct null_node_cache *
  */
 static struct null_node_cache *
-null_node_hash(targetvp)
-struct vnode *targetvp;
+null_node_hash(lowervp)
+struct vnode *lowervp;
 {
 {
-       return (&null_node_cache[NULL_NHASH(targetvp)]);
+       return (&null_node_cache[NULL_NHASH(lowervp)]);
 }
 
 /*
  * Make a new null_node node.
 }
 
 /*
  * Make a new null_node node.
- * Vp is the alias vnode, lofsvp is the target vnode.
- * Maintain a reference to (targetvp).
+ * Vp is the alias vnode, lofsvp is the lower vnode.
+ * Maintain a reference to (lowervp).
  */
 static void
  */
 static void
-null_node_alloc(vp, targetvp)
+null_node_alloc(vp, lowervp)
        struct vnode *vp;
        struct vnode *vp;
-       struct vnode *targetvp;
+       struct vnode *lowervp;
 {
        struct null_node_cache *hd;
        struct null_node *a;
 
 #ifdef NULLFS_DIAGNOSTIC
 {
        struct null_node_cache *hd;
        struct null_node *a;
 
 #ifdef NULLFS_DIAGNOSTIC
-       printf("null_node_alloc(%x, %x)\n", vp, targetvp);
+       printf("null_node_alloc(%x, %x)\n", vp, lowervp);
 #endif
 
        MALLOC(a, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
 #endif
 
        MALLOC(a, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
+       vp->v_type = lowervp->v_type;
        a->null_vnode = vp;
        vp->v_data = a;
        a->null_vnode = vp;
        vp->v_data = a;
-       VREF(targetvp);
-       a->null_lowervp = targetvp;
-       hd = null_node_hash(targetvp);
+       VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
+       a->null_lowervp = lowervp;
+       hd = null_node_hash(lowervp);
        insque(a, hd);
 
 #ifdef NULLFS_DIAGNOSTIC
        insque(a, hd);
 
 #ifdef NULLFS_DIAGNOSTIC
-       vprint("alloc vp", vp);
-       vprint("alloc targetvp", targetvp);
+       vprint("null_node_alloc vp", vp);
+       vprint("null_node_alloc lowervp", lowervp);
 #endif
 }
 
 #endif
 }
 
@@ -117,16 +118,16 @@ null_node_flushmp (mp)
 
        printf("null_node_flushmp (%x)\n", mp);
 
 
        printf("null_node_flushmp (%x)\n", mp);
 
-       roota = VTONULLNODE(MOUNTTONULLMOUNT(mp)->nullm_rootvp);
+       roota = VTONULL(MOUNTTONULLMOUNT(mp)->nullm_rootvp);
 
        for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++) {
                struct null_node *a = ac->ac_forw;
                while (a != (struct null_node *) ac) {
 
        for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++) {
                struct null_node *a = ac->ac_forw;
                while (a != (struct null_node *) ac) {
-                       if (a != roota && a->null_vnode->v_mount == mp) {
+                       if (a != roota && NULLTOV(a)->v_mount == mp) {
                                struct vnode *vp = a->null_lowervp;
                                if (vp) {
                                        a->null_lowervp = 0;
                                struct vnode *vp = a->null_lowervp;
                                if (vp) {
                                        a->null_lowervp = 0;
-                                       vprint("would vrele", vp);
+                                       vprint("null_flushmp: would vrele", vp);
                                        /*vrele(vp);*/
                                        i++;
                                }
                                        /*vrele(vp);*/
                                        i++;
                                }
@@ -140,75 +141,115 @@ null_node_flushmp (mp)
 #endif
 
 /*
 #endif
 
 /*
- * Return alias for target vnode if already exists, else 0.
+ * XXX - this should go elsewhere.
+ * Just like vget, but with no lock at the end.
  */
  */
-static struct null_node *
-null_node_find(mp, targetvp)
+int
+vget_nolock(vp)
+       register struct vnode *vp;
+{
+       extern struct vnode *vfreeh, **vfreet;
+       register struct vnode *vq;
+
+       if (vp->v_flag & VXLOCK) {
+               vp->v_flag |= VXWANT;
+               sleep((caddr_t)vp, PINOD);
+               return (1);
+       }
+       if (vp->v_usecount == 0) {
+               if (vq = vp->v_freef)
+                       vq->v_freeb = vp->v_freeb;
+               else
+                       vfreet = vp->v_freeb;
+               *vp->v_freeb = vq;
+               vp->v_freef = NULL;
+               vp->v_freeb = NULL;
+       }
+       VREF(vp);
+       /* VOP_LOCK(vp); */
+       return (0);
+}
+
+
+/*
+ * Return a VREF'ed alias for lower vnode if already exists, else 0.
+ */
+static struct vnode *
+null_node_find(mp, lowervp)
        struct mount *mp;
        struct mount *mp;
-       struct vnode *targetvp;
+       struct vnode *lowervp;
 {
        struct null_node_cache *hd;
        struct null_node *a;
 {
        struct null_node_cache *hd;
        struct null_node *a;
+       struct vnode *vp;
 
 #ifdef NULLFS_DIAGNOSTIC
 
 #ifdef NULLFS_DIAGNOSTIC
-       printf("null_node_find(mp = %x, target = %x)\n", mp, targetvp);
+       printf("null_node_find(mp = %x, lower = %x)\n", mp, lowervp);
 #endif
 
        /*
         * Find hash base, and then search the (two-way) linked
         * list looking for a null_node structure which is referencing
 #endif
 
        /*
         * Find hash base, and then search the (two-way) linked
         * list looking for a null_node structure which is referencing
-        * the target vnode.  If found, the increment the null_node
-        * reference count (but NOT the target vnode's VREF counter).
+        * the lower vnode.  If found, the increment the null_node
+        * reference count (but NOT the lower vnode's VREF counter).
         */
         */
-       hd = null_node_hash(targetvp);
-
+       hd = null_node_hash(lowervp);
+loop:
        for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
        for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
-               if (a->null_lowervp == targetvp && a->null_vnode->v_mount == mp) {
+               if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
 #ifdef NULLFS_DIAGNOSTIC
                        printf("null_node_find(%x): found (%x,%x)->%x\n",
 #ifdef NULLFS_DIAGNOSTIC
                        printf("null_node_find(%x): found (%x,%x)->%x\n",
-                               targetvp, mp, a->null_vnode, targetvp);
+                               lowervp, mp, NULLTOV(a), lowervp);
 #endif
 #endif
-                       return (a);
+                       vp = NULLTOV(a);
+                       /*
+                        * NEEDSWORK: Don't call the normal vget,
+                        * it will do a VOP_LOCK which is bypassed
+                        * and will lock against ourselves.
+                        * Unfortunately, we need vget for the VXLOCK
+                        * stuff.
+                        */
+                       if (vget_nolock(vp)) {
+                               printf ("null_node_find: vget failed.\n");
+                               goto loop;
+                       };
+                       return (vp);
                }
        }
 
 #ifdef NULLFS_DIAGNOSTIC
                }
        }
 
 #ifdef NULLFS_DIAGNOSTIC
-       printf("null_node_find(%x, %x): NOT found\n", mp, targetvp);
+       printf("null_node_find(%x, %x): NOT found\n", mp, lowervp);
 #endif
 
 #endif
 
-       return (0);
+       return NULL;
 }
 
 }
 
+#if 1
+int null_node_create_barrier = 1;
+#endif
+
 /*
  * Try to find an existing null_node vnode refering
  * to it, otherwise make a new null_node vnode which
 /*
  * Try to find an existing null_node vnode refering
  * to it, otherwise make a new null_node vnode which
- * contains a reference to the target vnode.
+ * contains a reference to the lower vnode.
  */
 int
  */
 int
-null_node_create(mp, targetvp, newvpp)
+null_node_create(mp, lowervp, newvpp)
        struct mount *mp;
        struct mount *mp;
-       struct vnode *targetvp;
+       struct vnode *lowervp;
        struct vnode **newvpp;
 {
        struct vnode **newvpp;
 {
-       struct null_node *ap;
        struct vnode *aliasvp;
 
        struct vnode *aliasvp;
 
-       if (targetvp->v_type != VDIR || targetvp->v_op == null_vnodeop_p) {
-               *newvpp = targetvp;
-               return;
-       }
-
-       ap = null_node_find(mp, targetvp);
-
-       if (ap) {
+       if (aliasvp = null_node_find(mp, lowervp)) {
                /*
                /*
-                * Take another reference to the alias vnode
+                * null_node_find has taken another reference
+                * to the alias vnode.
                 */
 #ifdef NULLFS_DIAGNOSTIC
                 */
 #ifdef NULLFS_DIAGNOSTIC
-               vprint("null_node_alias: exists", ap->null_vnode);
+               vprint("null_node_create: exists", NULLTOV(ap));
 #endif
 #endif
-               aliasvp = ap->null_vnode;
-               VREF(aliasvp);
+               /* VREF(aliasvp); --- done in null_node_find */
        } else {
                int error;
 
        } else {
                int error;
 
@@ -216,58 +257,86 @@ null_node_create(mp, targetvp, newvpp)
                 * Get new vnode.
                 */
 #ifdef NULLFS_DIAGNOSTIC
                 * Get new vnode.
                 */
 #ifdef NULLFS_DIAGNOSTIC
-               printf("null_node_alias: create new alias vnode\n");
+               printf("null_node_create: create new alias vnode\n");
 #endif
                if (error = getnewvnode(VT_UFS, mp, null_vnodeop_p, &aliasvp))
 #endif
                if (error = getnewvnode(VT_UFS, mp, null_vnodeop_p, &aliasvp))
-                       return (error); /* XXX: VT_LOFS above */
-
-               /*
-                * Must be a directory
-                */
-               aliasvp->v_type = VDIR;
+                       return (error); /* XXX: VT_NULL above */
 
                /*
                 * Make new vnode reference the null_node.
                 */
 
                /*
                 * Make new vnode reference the null_node.
                 */
-               null_node_alloc(aliasvp, targetvp);
+               null_node_alloc(aliasvp, lowervp);
 
                /*
                 * aliasvp is already VREF'd by getnewvnode()
                 */
        }
 
 
                /*
                 * aliasvp is already VREF'd by getnewvnode()
                 */
        }
 
-       vrele(targetvp);
+       vrele(lowervp);
+
+       if (lowervp->v_usecount < 1) {
+               vprint ("null_node_create: alias ");
+               vprint ("null_node_create: lower ");
+               printf ("null_node_create: lower has 0 usecount.\n");
+               while (null_node_create_barrier) /* wait */ ;
+               panic ("null_node_create: lower has 0 usecount.");
+       };
 
 #ifdef NULLFS_DIAGNOSTIC
 
 #ifdef NULLFS_DIAGNOSTIC
-       vprint("null_node_alias alias", aliasvp);
-       vprint("null_node_alias target", targetvp);
+       vprint("null_node_create: alias", aliasvp);
+       vprint("null_node_create: lower", lowervp);
 #endif
 
        *newvpp = aliasvp;
        return (0);
 }
 #ifdef NULLFS_DIAGNOSTIC
 #endif
 
        *newvpp = aliasvp;
        return (0);
 }
 #ifdef NULLFS_DIAGNOSTIC
+int null_checkvp_barrier = 1;
 struct vnode *
 null_checkvp(vp, fil, lno)
        struct vnode *vp;
        char *fil;
        int lno;
 {
 struct vnode *
 null_checkvp(vp, fil, lno)
        struct vnode *vp;
        char *fil;
        int lno;
 {
-       struct null_node *a = VTONULLNODE(vp);
-       if (a->null_lowervp == 0) {
+       struct null_node *a = VTONULL(vp);
+#if 0
+       /*
+        * Can't do this check because vop_reclaim runs
+        * with a funny vop vector.
+        */
+       if (vp->v_op != null_vnodeop_p) {
+               printf ("null_checkvp: on non-null-node\n");
+               while (null_checkvp_barrier) /*WAIT*/ ;
+               panic("null_checkvp");
+       };
+#endif
+       if (a->null_lowervp == NULL) {
                /* Should never happen */
                int i; u_long *p;
                printf("vp = %x, ZERO ptr\n", vp);
                for (p = (u_long *) a, i = 0; i < 8; i++)
                        printf(" %x", p[i]);
                printf("\n");
                /* Should never happen */
                int i; u_long *p;
                printf("vp = %x, ZERO ptr\n", vp);
                for (p = (u_long *) a, i = 0; i < 8; i++)
                        printf(" %x", p[i]);
                printf("\n");
-               DELAY(2000000);
+               /* wait for debugger */
+               while (null_checkvp_barrier) /*WAIT*/ ;
                panic("null_checkvp");
        }
                panic("null_checkvp");
        }
-       printf("nullvp %x/%d -> %x/%d [%s, %d]\n",
-               a->null_vnode, a->null_vnode->v_usecount,
+       if (a->null_lowervp->v_usecount < 1) {
+               int i; u_long *p;
+               printf("vp = %x, unref'ed lowervp\n", vp);
+               for (p = (u_long *) a, i = 0; i < 8; i++)
+                       printf(" %x", p[i]);
+               printf("\n");
+               /* wait for debugger */
+               while (null_checkvp_barrier) /*WAIT*/ ;
+               panic ("null with unref'ed lowervp");
+       };
+#if 0
+       printf("null %x/%d -> %x/%d [%s, %d]\n",
+               NULLTOV(a), NULLTOV(a)->v_usecount,
                a->null_lowervp, a->null_lowervp->v_usecount,
                fil, lno);
                a->null_lowervp, a->null_lowervp->v_usecount,
                fil, lno);
+#endif
        return a->null_lowervp;
 }
 #endif
        return a->null_lowervp;
 }
 #endif
index a9a6d58..d2f1e3e 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)null_vfsops.c       1.4 (Berkeley) %G%
+ *     @(#)null_vfsops.c       1.5 (Berkeley) %G%
  *
  * @(#)lofs_vfsops.c   1.2 (Berkeley) 6/18/92
  * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
  *
  * @(#)lofs_vfsops.c   1.2 (Berkeley) 6/18/92
  * $Id: lofs_vfsops.c,v 1.9 1992/05/30 10:26:24 jsp Exp jsp $
@@ -66,7 +66,7 @@ nullfs_mount(mp, path, data, ndp, p)
                return (error);
 
        /*
                return (error);
 
        /*
-        * Find target node
+        * Find lower node
         */
        NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
                UIO_USERSPACE, args.target, p);
         */
        NDINIT(ndp, LOOKUP, FOLLOW|WANTPARENT|LOCKLEAF,
                UIO_USERSPACE, args.target, p);
@@ -74,7 +74,7 @@ nullfs_mount(mp, path, data, ndp, p)
                return (error);
 
        /*
                return (error);
 
        /*
-        * Sanity check on target vnode
+        * Sanity check on lower vnode
         */
        lowerrootvp = ndp->ni_vp;
 #ifdef NULLFS_DIAGNOSTIC
         */
        lowerrootvp = ndp->ni_vp;
 #ifdef NULLFS_DIAGNOSTIC
@@ -84,7 +84,7 @@ nullfs_mount(mp, path, data, ndp, p)
        ndp->ni_dvp = 0;
 
        /*
        ndp->ni_dvp = 0;
 
        /*
-        * NEEDSWORK: Is this really bad, or just not
+        * NEEDSWORK: Is this check really necessary, or just not
         * the way it's been?
         */
        if (lowerrootvp->v_type != VDIR) {
         * the way it's been?
         */
        if (lowerrootvp->v_type != VDIR) {
@@ -100,7 +100,7 @@ nullfs_mount(mp, path, data, ndp, p)
                                M_UFSMNT, M_WAITOK);    /* XXX */
 
        /*
                                M_UFSMNT, M_WAITOK);    /* XXX */
 
        /*
-        * Save reference to underlying target FS
+        * Save reference to underlying lower FS
         */
        amp->nullm_vfs = lowerrootvp->v_mount;
 
         */
        amp->nullm_vfs = lowerrootvp->v_mount;
 
@@ -110,7 +110,7 @@ nullfs_mount(mp, path, data, ndp, p)
         */
        error = null_node_create(mp, lowerrootvp, &vp);
        /*
         */
        error = null_node_create(mp, lowerrootvp, &vp);
        /*
-        * Unlock the node (either the target or the alias)
+        * Unlock the node (either the lower or the alias)
         */
        VOP_UNLOCK(vp);
        /*
         */
        VOP_UNLOCK(vp);
        /*
@@ -140,7 +140,7 @@ nullfs_mount(mp, path, data, ndp, p)
            &size);
        bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
 #ifdef NULLFS_DIAGNOSTIC
            &size);
        bzero(mp->mnt_stat.f_mntfromname + size, MNAMELEN - size);
 #ifdef NULLFS_DIAGNOSTIC
-       printf("nullfs_mount: target %s, alias at %s\n",
+       printf("nullfs_mount: lower %s, alias at %s\n",
                mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
 #endif
        return (0);
                mp->mnt_stat.f_mntfromname, mp->mnt_stat.f_mntonname);
 #endif
        return (0);
@@ -209,7 +209,7 @@ nullfs_unmount(mp, mntflags, p)
 #endif
 
 #ifdef NULLFS_DIAGNOSTIC
 #endif
 
 #ifdef NULLFS_DIAGNOSTIC
-       vprint("alias root of target", nullm_rootvp);
+       vprint("alias root of lower", nullm_rootvp);
 #endif  
        /*
         * Release reference on underlying root vnode
 #endif  
        /*
         * Release reference on underlying root vnode
index ccb54cc..625d112 100644 (file)
@@ -8,7 +8,7 @@
  *
  * %sccs.include.redist.c%
  *
  *
  * %sccs.include.redist.c%
  *
- *     @(#)null_vnops.c        1.4 (Berkeley) %G%
+ *     @(#)null_vnops.c        1.5 (Berkeley) %G%
  *
  * Ancestors:
  *     @(#)lofs_vnops.c        1.2 (Berkeley) 6/18/92
  *
  * Ancestors:
  *     @(#)lofs_vnops.c        1.2 (Berkeley) 6/18/92
@@ -40,6 +40,7 @@
  *
  * INVOKING OPERATIONS ON LOWER LAYERS
  *
  *
  * INVOKING OPERATIONS ON LOWER LAYERS
  *
+ * 
  * NEEDSWORK: Describe methods to invoke operations on the lower layer
  * (bypass vs. VOP).
  *
  * NEEDSWORK: Describe methods to invoke operations on the lower layer
  * (bypass vs. VOP).
  *
@@ -113,8 +114,8 @@ null_bypass(ap)
        /*
         * We require at least one vp.
         */
        /*
         * We require at least one vp.
         */
-       if (descp->vdesc_vp_offsets==NULL ||
-           descp->vdesc_vp_offsets[0]==VDESC_NO_OFFSET)
+       if (descp->vdesc_vp_offsets == NULL ||
+           descp->vdesc_vp_offsets[0] == VDESC_NO_OFFSET)
                panic ("null_bypass: no vp's in map.\n");
 #endif
 
                panic ("null_bypass: no vp's in map.\n");
 #endif
 
@@ -124,8 +125,8 @@ null_bypass(ap)
         * the first mapped vnode's operation vector.
         */
        reles = descp->vdesc_flags;
         * the first mapped vnode's operation vector.
         */
        reles = descp->vdesc_flags;
-       for (i=0; i<VDESC_MAX_VPS; reles>>=1, i++) {
-               if (descp->vdesc_vp_offsets[i]==VDESC_NO_OFFSET)
+       for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+               if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
                        break;   /* bail out at end of list */
                vps_p[i] = this_vp_p = 
                        VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
                        break;   /* bail out at end of list */
                vps_p[i] = this_vp_p = 
                        VOPARG_OFFSETTO(struct vnode**,descp->vdesc_vp_offsets[i],ap);
@@ -139,11 +140,16 @@ null_bypass(ap)
                } else {
                        old_vps[i] = *this_vp_p;
                        *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p);
                } else {
                        old_vps[i] = *this_vp_p;
                        *(vps_p[i]) = NULLVPTOLOWERVP(*this_vp_p);
+                       /*
+                        * XXX - Several operations have the side effect
+                        * of vrele'ing their vp's.  We must account for
+                        * that.  (This should go away in the future.)
+                        */
                        if (reles & 1)
                                VREF(*this_vp_p);
                        if (reles & 1)
                                VREF(*this_vp_p);
-               };
+               }
                        
                        
-       };
+       }
 
        /*
         * Call the operation on the lower layer
 
        /*
         * Call the operation on the lower layer
@@ -157,26 +163,40 @@ null_bypass(ap)
         * to their original value.
         */
        reles = descp->vdesc_flags;
         * to their original value.
         */
        reles = descp->vdesc_flags;
-       for (i=0; i<VDESC_MAX_VPS; reles>>=1, i++) {
-               if (descp->vdesc_vp_offsets[i]==VDESC_NO_OFFSET)
+       for (i = 0; i < VDESC_MAX_VPS; reles >>= 1, i++) {
+               if (descp->vdesc_vp_offsets[i] == VDESC_NO_OFFSET)
                        break;   /* bail out at end of list */
                if (old_vps[i]) {
                        *(vps_p[i]) = old_vps[i];
                        if (reles & 1)
                                vrele(*(vps_p[i]));
                        break;   /* bail out at end of list */
                if (old_vps[i]) {
                        *(vps_p[i]) = old_vps[i];
                        if (reles & 1)
                                vrele(*(vps_p[i]));
-               };
-       };
+               }
+       }
 
        /*
 
        /*
-        * Map the possible out-going vpp.
+        * Map the possible out-going vpp
+        * (Assumes that the lower layer always returns
+        * a VREF'ed vpp unless it gets an error.)
         */
        if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
            !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
            !error) {
         */
        if (descp->vdesc_vpp_offset != VDESC_NO_OFFSET &&
            !(descp->vdesc_flags & VDESC_NOMAP_VPP) &&
            !error) {
-               vppp=VOPARG_OFFSETTO(struct vnode***,
+               /*
+                * XXX - even though some ops have vpp returned vp's,
+                * several ops actually vrele this before returning.
+                * We must avoid these ops.
+                * (This should go away.)
+                */
+               if (descp->vdesc_flags & VDESC_VPP_WILLRELE) {
+#ifdef NULLFS_DIAGNOSTIC
+                       printf("null_bypass (%s), lowervpp->usecount = %d\n", vdesc->vdesc_name, (**vppp)->v_usecount);
+#endif
+                       return (error);
+               }
+               vppp = VOPARG_OFFSETTO(struct vnode***,
                                 descp->vdesc_vpp_offset,ap);
                error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp);
                                 descp->vdesc_vpp_offset,ap);
                error = null_node_create(old_vps[0]->v_mount, **vppp, *vppp);
-       };
+       }
 
        return (error);
 }
 
        return (error);
 }
@@ -190,20 +210,42 @@ null_getattr(ap)
        struct vop_getattr_args *ap;
 {
        int error;
        struct vop_getattr_args *ap;
 {
        int error;
-       if (error=null_bypass(ap))
+       if (error = null_bypass(ap))
                return error;
        /* Requires that arguments be restored. */
        ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
        return 0;
 }
 
                return error;
        /* Requires that arguments be restored. */
        ap->a_vap->va_fsid = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0];
        return 0;
 }
 
+/*
+ * XXX - Ideally inactive does not release the lowervp
+ * so the null_node can stay around in the cache and be reused.
+ * Unfortunately, this currently causes "locking against self"
+ * problems in the UFS, so currently AVOID_CACHING hacks
+ * around the bug.
+ */
+#define AVOID_CACHING
 
 int
 null_inactive (ap)
        struct vop_inactive_args *ap;
 {
 
 int
 null_inactive (ap)
        struct vop_inactive_args *ap;
 {
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_inactive(ap->a_vp = %x->%x)\n", ap->a_vp, NULLVPTOLOWERVP(ap->a_vp));
+#ifdef AVOID_CACHING
+       struct vnode *vp = ap->a_vp;
+       struct null_node *xp = VTONULL(vp);
+       struct vnode *lowervp = xp->null_lowervp;
+
+       xp->null_lowervp = NULL;
+       remque(xp);
+       FREE(vp->v_data, M_TEMP);
+       vp->v_data = NULL;
+       vp->v_type = VBAD;   /* The node is clean (no reclaim needed). */
+       vrele (lowervp);
+#else
+#ifdef DIAGNOSTIC
+       if (VOP_ISLOCKED(ap->a_vp)) {
+               panic ("null_inactive: inactive node is locked.");
+       };
 #endif
        /*
         * Do nothing (and _don't_ bypass).
 #endif
        /*
         * Do nothing (and _don't_ bypass).
@@ -217,22 +259,40 @@ null_inactive (ap)
         * That's too much work for now.
         */
        return 0;
         * That's too much work for now.
         */
        return 0;
+#endif
 }
 
 }
 
+int
 null_reclaim (ap)
        struct vop_reclaim_args *ap;
 {
 null_reclaim (ap)
        struct vop_reclaim_args *ap;
 {
-       struct vnode *targetvp;
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_reclaim(ap->a_vp = %x->%x)\n", ap->a_vp, NULLVPTOLOWERVP(ap->a_vp));
+       struct vnode *vp = ap->a_vp;
+       struct null_node *xp = VTONULL(vp);
+       struct vnode *lowervp = xp->null_lowervp;
+
+#ifdef AVOID_CACHING
+       return 0;
+#else
+       /*
+        * Note: at this point, vp->v_op == dead_vnodeop_p,
+        * so we can't call VOPs on ourself.
+        */
+       /* After this assignment, this node will not be re-used. */
+#ifdef DIAGNOSTIC
+       if (lowervp->v_usecount == 1 && ISLOCKED(lowervp)) {
+               panic("null_reclaim: lowervp is locked but must go away.");
+       };
+#endif
+       xp->null_lowervp = NULL;
+       remque(xp);
+       FREE(vp->v_data, M_TEMP);
+       vp->v_data = NULL;
+       vrele (lowervp);
+       return 0;
 #endif
 #endif
-       remque(VTONULL(ap->a_vp));           /* NEEDSWORK: What? */
-       vrele (NULLVPTOLOWERVP(ap->a_vp));   /* release lower layer */
-       FREE(ap->a_vp->v_data, M_TEMP);
-       ap->a_vp->v_data = 0;
-       return (0);
 }
 
 }
 
+int
 null_bmap (ap)
        struct vop_bmap_args *ap;
 {
 null_bmap (ap)
        struct vop_bmap_args *ap;
 {
@@ -243,6 +303,7 @@ null_bmap (ap)
        return VOP_BMAP(NULLVPTOLOWERVP(ap->a_vp), ap->a_bn, ap->a_vpp, ap->a_bnp);
 }
 
        return VOP_BMAP(NULLVPTOLOWERVP(ap->a_vp), ap->a_bn, ap->a_vpp, ap->a_bnp);
 }
 
+int
 null_strategy (ap)
        struct vop_strategy_args *ap;
 {
 null_strategy (ap)
        struct vop_strategy_args *ap;
 {
@@ -268,7 +329,7 @@ null_print (ap)
        struct vop_print_args *ap;
 {
        register struct vnode *vp = ap->a_vp;
        struct vop_print_args *ap;
 {
        register struct vnode *vp = ap->a_vp;
-       printf ("tag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp));
+       printf ("\ttag VT_NULLFS, vp=%x, lowervp=%x\n", vp, NULLVPTOLOWERVP(vp));
        return 0;
 }
 
        return 0;
 }