BSD 4_4 release
[unix-history] / usr / src / sys / miscfs / nullfs / null_subr.c
index d31ff93..fca9ef7 100644 (file)
@@ -1,14 +1,40 @@
 /*
 /*
- * Copyright (c) 1992 The Regents of the University of California
- * Copyright (c) 1990, 1992 Jan-Simon Pendry
+ * Copyright (c) 1992, 1993
+ *     The Regents of the University of California.  All rights reserved.
  * All rights reserved.
  *
  * This code is derived from software donated to Berkeley by
  * Jan-Simon Pendry.
  *
  * All rights reserved.
  *
  * This code is derived from software donated to Berkeley by
  * Jan-Simon Pendry.
  *
- * %sccs.include.redist.c%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
  *
  *
- *     @(#)lofs_subr.c 1.2 (Berkeley) 6/18/92
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)null_subr.c 8.1 (Berkeley) 6/10/93
  *
  * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
  */
  *
  * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
  */
@@ -21,7 +47,7 @@
 #include <sys/mount.h>
 #include <sys/namei.h>
 #include <sys/malloc.h>
 #include <sys/mount.h>
 #include <sys/namei.h>
 #include <sys/malloc.h>
-#include <lofs/lofs.h>
+#include <miscfs/nullfs/null.h>
 
 #define LOG2_SIZEVNODE 7               /* log2(sizeof struct vnode) */
 #define        NNULLNODECACHE 16
 
 #define LOG2_SIZEVNODE 7               /* log2(sizeof struct vnode) */
 #define        NNULLNODECACHE 16
 
 /*
  * 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,155 +86,154 @@ 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)]);
-}
 
 
-/*
- * Make a new null_node node.
- * Vp is the alias vnode, lofsvp is the target vnode.
- * Maintain a reference to (targetvp).
- */
-static void
-null_node_alloc(vp, targetvp)
-       struct vnode *vp;
-       struct vnode *targetvp;
-{
-       struct null_node_cache *hd;
-       struct null_node *a;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_node_alloc(%x, %x)\n", vp, targetvp);
-#endif
-
-       MALLOC(a, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
-       a->null_vnode = vp;
-       vp->v_data = a;
-       VREF(targetvp);
-       a->null_lowervp = targetvp;
-       hd = null_node_hash(targetvp);
-       insque(a, hd);
-
-#ifdef NULLFS_DIAGNOSTIC
-       vprint("alloc vp", vp);
-       vprint("alloc targetvp", targetvp);
-#endif
+       return (&null_node_cache[NULL_NHASH(lowervp)]);
 }
 
 }
 
-#ifdef NULLFS_DIAGNOSTIC
+
 /*
 /*
- * NEEDSWORK:  The ability to set lowervp to null here
- * implies that one can never count on lowervp staying null
- * (even if vp is locked).  This seems quite bad.  Think
- * about these things.
+ * XXX - this should go elsewhere.
+ * Just like vget, but with no lock at the end.
  */
  */
-void
-null_node_flushmp (mp)
-       struct mount *mp;
+int
+vget_nolock(vp)
+       register struct vnode *vp;
 {
 {
-       struct null_node_cache *ac;
-       int i = 0;
-       struct null_node *roota;
+       extern struct vnode *vfreeh, **vfreet;
+       register struct vnode *vq;
 
 
-       printf("null_node_flushmp (%x)\n", mp);
-
-       roota = VTONULLNODE(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) {
-                       if (a != roota && a->null_vnode->v_mount == mp) {
-                               struct vnode *vp = a->null_lowervp;
-                               if (vp) {
-                                       a->null_lowervp = 0;
-                                       vprint("would vrele", vp);
-                                       /*vrele(vp);*/
-                                       i++;
-                               }
-                       }
-                       a = a->null_forw;
-               }
+       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;
        }
        }
-       if (i > 0)
-               printf("null_node: vrele'd %d aliases\n", i);
+       VREF(vp);
+       /* VOP_LOCK(vp); */
+       return (0);
 }
 }
-#endif
+
 
 /*
 
 /*
- * Return alias for target vnode if already exists, else 0.
+ * Return a VREF'ed alias for lower vnode if already exists, else 0.
  */
  */
-static struct null_node *
-null_node_find(mp, targetvp)
+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;
-
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_node_find(mp = %x, target = %x)\n", mp, targetvp);
-#endif
+       struct vnode *vp;
 
        /*
         * Find hash base, and then search the (two-way) linked
         * list looking for a null_node structure which is referencing
 
        /*
         * 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) {
-#ifdef NULLFS_DIAGNOSTIC
-                       printf("null_node_find(%x): found (%x,%x)->%x\n",
-                               targetvp, mp, a->null_vnode, targetvp);
-#endif
-                       return (a);
+               if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
+                       vp = NULLTOV(a);
+                       /*
+                        * We need vget for the VXLOCK
+                        * stuff, but we don't want to lock
+                        * the lower node.
+                        */
+                       if (vget_nolock(vp)) {
+                               printf ("null_node_find: vget failed.\n");
+                               goto loop;
+                       };
+                       return (vp);
                }
        }
 
                }
        }
 
-#ifdef NULLFS_DIAGNOSTIC
-       printf("null_node_find(%x, %x): NOT found\n", mp, targetvp);
-#endif
+       return NULL;
+}
 
 
-       return (0);
+
+/*
+ * Make a new null_node node.
+ * Vp is the alias vnode, lofsvp is the lower vnode.
+ * Maintain a reference to (lowervp).
+ */
+static int
+null_node_alloc(mp, lowervp, vpp)
+       struct mount *mp;
+       struct vnode *lowervp;
+       struct vnode **vpp;
+{
+       struct null_node_cache *hd;
+       struct null_node *xp;
+       struct vnode *othervp, *vp;
+       int error;
+
+       if (error = getnewvnode(VT_UFS, mp, null_vnodeop_p, vpp))
+               return (error); /* XXX: VT_NULL above */
+       vp = *vpp;
+
+       MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
+       vp->v_type = lowervp->v_type;
+       xp->null_vnode = vp;
+       vp->v_data = xp;
+       xp->null_lowervp = lowervp;
+       /*
+        * Before we insert our new node onto the hash chains,
+        * check to see if someone else has beaten us to it.
+        * (We could have slept in MALLOC.)
+        */
+       if (othervp = null_node_find(lowervp)) {
+               FREE(xp, M_TEMP);
+               vp->v_type = VBAD;      /* node is discarded */
+               vp->v_usecount = 0;     /* XXX */
+               *vpp = othervp;
+               return 0;
+       };
+       VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
+       hd = null_node_hash(lowervp);
+       insque(xp, hd);
+       return 0;
 }
 
 }
 
+
 /*
  * 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,31 +241,35 @@ 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
 #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;
 
                /*
                 * Make new vnode reference the null_node.
                 */
 
                /*
                 * Make new vnode reference the null_node.
                 */
-               null_node_alloc(aliasvp, targetvp);
+               if (error = null_node_alloc(mp, lowervp, &aliasvp))
+                       return error;
 
                /*
                 * aliasvp is already VREF'd by getnewvnode()
                 */
        }
 
 
                /*
                 * aliasvp is already VREF'd by getnewvnode()
                 */
        }
 
-       vrele(targetvp);
+       vrele(lowervp);
+
+#ifdef DIAGNOSTIC
+       if (lowervp->v_usecount < 1) {
+               /* Should never happen... */
+               vprint ("null_node_create: alias ");
+               vprint ("null_node_create: lower ");
+               printf ("null_node_create: lower has 0 usecount.\n");
+               panic ("null_node_create: lower has 0 usecount.");
+       };
+#endif
 
 #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;
 #endif
 
        *newvpp = aliasvp;
@@ -253,21 +282,45 @@ null_checkvp(vp, fil, lno)
        char *fil;
        int lno;
 {
        char *fil;
        int lno;
 {
-       struct null_node *a = VTONULLNODE(vp);
-       if (a->null_lowervp == 0) {
+       struct null_node *a = VTONULL(vp);
+#ifdef notyet
+       /*
+        * 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");
+       };
+#ifdef notyet
+       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