1df8bae1dSRodney W. Grimes /* 2df8bae1dSRodney W. Grimes * Copyright (c) 1992, 1993 3df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 4df8bae1dSRodney W. Grimes * 5df8bae1dSRodney W. Grimes * This code is derived from software donated to Berkeley by 6df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 7df8bae1dSRodney W. Grimes * 8df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 9df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 10df8bae1dSRodney W. Grimes * are met: 11df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 12df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 13df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 14df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 15df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 16df8bae1dSRodney W. Grimes * 3. All advertising materials mentioning features or use of this software 17df8bae1dSRodney W. Grimes * must display the following acknowledgement: 18df8bae1dSRodney W. Grimes * This product includes software developed by the University of 19df8bae1dSRodney W. Grimes * California, Berkeley and its contributors. 20df8bae1dSRodney W. Grimes * 4. Neither the name of the University nor the names of its contributors 21df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 22df8bae1dSRodney W. Grimes * without specific prior written permission. 23df8bae1dSRodney W. Grimes * 24df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34df8bae1dSRodney W. Grimes * SUCH DAMAGE. 35df8bae1dSRodney W. Grimes * 36996c772fSJohn Dyson * @(#)null_subr.c 8.7 (Berkeley) 5/14/95 37df8bae1dSRodney W. Grimes * 38c3aac50fSPeter Wemm * $FreeBSD$ 39df8bae1dSRodney W. Grimes */ 40df8bae1dSRodney W. Grimes 41df8bae1dSRodney W. Grimes #include <sys/param.h> 42df8bae1dSRodney W. Grimes #include <sys/systm.h> 438da80660SBoris Popov #include <sys/kernel.h> 44fb919e4dSMark Murray #include <sys/lock.h> 45df8bae1dSRodney W. Grimes #include <sys/malloc.h> 46fb919e4dSMark Murray #include <sys/mount.h> 47fb919e4dSMark Murray #include <sys/proc.h> 48fb919e4dSMark Murray #include <sys/vnode.h> 49fb919e4dSMark Murray 5099d300a1SRuslan Ermilov #include <fs/nullfs/null.h> 51df8bae1dSRodney W. Grimes 52df8bae1dSRodney W. Grimes #define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */ 53df8bae1dSRodney W. Grimes #define NNULLNODECACHE 16 54df8bae1dSRodney W. Grimes 55df8bae1dSRodney W. Grimes /* 56df8bae1dSRodney W. Grimes * Null layer cache: 57df8bae1dSRodney W. Grimes * Each cache entry holds a reference to the lower vnode 58df8bae1dSRodney W. Grimes * along with a pointer to the alias vnode. When an 59df8bae1dSRodney W. Grimes * entry is added the lower vnode is VREF'd. When the 60df8bae1dSRodney W. Grimes * alias is removed the lower vnode is vrele'd. 61df8bae1dSRodney W. Grimes */ 62df8bae1dSRodney W. Grimes 63996c772fSJohn Dyson #define NULL_NHASH(vp) \ 64a23d65bfSBruce Evans (&null_node_hashtbl[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & null_node_hash]) 658da80660SBoris Popov 66e3975643SJake Burkholder static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl; 67303b270bSEivind Eklund static u_long null_node_hash; 688da80660SBoris Popov struct lock null_hashlock; 698da80660SBoris Popov 708da80660SBoris Popov static MALLOC_DEFINE(M_NULLFSHASH, "NULLFS hash", "NULLFS hash table"); 718da80660SBoris Popov MALLOC_DEFINE(M_NULLFSNODE, "NULLFS node", "NULLFS vnode private part"); 72df8bae1dSRodney W. Grimes 737da1e3f0SBoris Popov static int null_node_alloc(struct mount *mp, struct vnode *lowervp, 747da1e3f0SBoris Popov struct vnode **vpp); 759b5e8b3aSBruce Evans static struct vnode * 767da1e3f0SBoris Popov null_node_find(struct mount *mp, struct vnode *lowervp); 779b5e8b3aSBruce Evans 78df8bae1dSRodney W. Grimes /* 79df8bae1dSRodney W. Grimes * Initialise cache headers 80df8bae1dSRodney W. Grimes */ 8126f9a767SRodney W. Grimes int 82996c772fSJohn Dyson nullfs_init(vfsp) 83996c772fSJohn Dyson struct vfsconf *vfsp; 84df8bae1dSRodney W. Grimes { 85996c772fSJohn Dyson 868da80660SBoris Popov NULLFSDEBUG("nullfs_init\n"); /* printed during system boot */ 878da80660SBoris Popov null_node_hashtbl = hashinit(NNULLNODECACHE, M_NULLFSHASH, &null_node_hash); 888da80660SBoris Popov lockinit(&null_hashlock, PVFS, "nullhs", 0, 0); 898da80660SBoris Popov return (0); 908da80660SBoris Popov } 918da80660SBoris Popov 928da80660SBoris Popov int 938da80660SBoris Popov nullfs_uninit(vfsp) 948da80660SBoris Popov struct vfsconf *vfsp; 958da80660SBoris Popov { 968da80660SBoris Popov 97a18b1f1dSJason Evans if (null_node_hashtbl) { 98a18b1f1dSJason Evans lockdestroy(&null_hashlock); 998da80660SBoris Popov free(null_node_hashtbl, M_NULLFSHASH); 100a18b1f1dSJason Evans } 10126f9a767SRodney W. Grimes return (0); 102df8bae1dSRodney W. Grimes } 103df8bae1dSRodney W. Grimes 104df8bae1dSRodney W. Grimes /* 105df8bae1dSRodney W. Grimes * Return a VREF'ed alias for lower vnode if already exists, else 0. 1064451405fSBoris Popov * Lower vnode should be locked on entry and will be left locked on exit. 107df8bae1dSRodney W. Grimes */ 108df8bae1dSRodney W. Grimes static struct vnode * 109df8bae1dSRodney W. Grimes null_node_find(mp, lowervp) 110df8bae1dSRodney W. Grimes struct mount *mp; 111df8bae1dSRodney W. Grimes struct vnode *lowervp; 112df8bae1dSRodney W. Grimes { 113996c772fSJohn Dyson struct proc *p = curproc; /* XXX */ 114996c772fSJohn Dyson struct null_node_hashhead *hd; 115df8bae1dSRodney W. Grimes struct null_node *a; 116df8bae1dSRodney W. Grimes struct vnode *vp; 117df8bae1dSRodney W. Grimes 118df8bae1dSRodney W. Grimes /* 119df8bae1dSRodney W. Grimes * Find hash base, and then search the (two-way) linked 120df8bae1dSRodney W. Grimes * list looking for a null_node structure which is referencing 121df8bae1dSRodney W. Grimes * the lower vnode. If found, the increment the null_node 122df8bae1dSRodney W. Grimes * reference count (but NOT the lower vnode's VREF counter). 123df8bae1dSRodney W. Grimes */ 124996c772fSJohn Dyson hd = NULL_NHASH(lowervp); 125df8bae1dSRodney W. Grimes loop: 1268da80660SBoris Popov lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p); 127fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(a, hd, null_hash) { 128df8bae1dSRodney W. Grimes if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) { 129df8bae1dSRodney W. Grimes vp = NULLTOV(a); 1308da80660SBoris Popov lockmgr(&null_hashlock, LK_RELEASE, NULL, p); 131df8bae1dSRodney W. Grimes /* 132df8bae1dSRodney W. Grimes * We need vget for the VXLOCK 133df8bae1dSRodney W. Grimes * stuff, but we don't want to lock 134df8bae1dSRodney W. Grimes * the lower node. 135df8bae1dSRodney W. Grimes */ 1364451405fSBoris Popov if (vget(vp, LK_EXCLUSIVE | LK_CANRECURSE, p)) { 137df8bae1dSRodney W. Grimes printf ("null_node_find: vget failed.\n"); 138df8bae1dSRodney W. Grimes goto loop; 139df8bae1dSRodney W. Grimes }; 1404451405fSBoris Popov /* 1414451405fSBoris Popov * Now we got both vnodes locked, so release the 1424451405fSBoris Popov * lower one. 1434451405fSBoris Popov */ 1444451405fSBoris Popov VOP_UNLOCK(lowervp, 0, p); 145df8bae1dSRodney W. Grimes return (vp); 146df8bae1dSRodney W. Grimes } 147df8bae1dSRodney W. Grimes } 1488da80660SBoris Popov lockmgr(&null_hashlock, LK_RELEASE, NULL, p); 149df8bae1dSRodney W. Grimes 150c5e17d9eSKATO Takenori return NULLVP; 151df8bae1dSRodney W. Grimes } 152df8bae1dSRodney W. Grimes 153df8bae1dSRodney W. Grimes 154df8bae1dSRodney W. Grimes /* 155df8bae1dSRodney W. Grimes * Make a new null_node node. 156df8bae1dSRodney W. Grimes * Vp is the alias vnode, lofsvp is the lower vnode. 157df8bae1dSRodney W. Grimes * Maintain a reference to (lowervp). 158df8bae1dSRodney W. Grimes */ 159df8bae1dSRodney W. Grimes static int 160df8bae1dSRodney W. Grimes null_node_alloc(mp, lowervp, vpp) 161df8bae1dSRodney W. Grimes struct mount *mp; 162df8bae1dSRodney W. Grimes struct vnode *lowervp; 163df8bae1dSRodney W. Grimes struct vnode **vpp; 164df8bae1dSRodney W. Grimes { 1658da80660SBoris Popov struct proc *p = curproc; /* XXX */ 166996c772fSJohn Dyson struct null_node_hashhead *hd; 167df8bae1dSRodney W. Grimes struct null_node *xp; 168df8bae1dSRodney W. Grimes struct vnode *othervp, *vp; 169df8bae1dSRodney W. Grimes int error; 170df8bae1dSRodney W. Grimes 1712f9bae59SDavid Greenman /* 1722f9bae59SDavid Greenman * Do the MALLOC before the getnewvnode since doing so afterward 1732f9bae59SDavid Greenman * might cause a bogus v_data pointer to get dereferenced 1742f9bae59SDavid Greenman * elsewhere if MALLOC should block. 1752f9bae59SDavid Greenman */ 1768da80660SBoris Popov MALLOC(xp, struct null_node *, sizeof(struct null_node), 1778da80660SBoris Popov M_NULLFSNODE, M_WAITOK); 1782f9bae59SDavid Greenman 179623ae52eSPoul-Henning Kamp error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp); 1802f9bae59SDavid Greenman if (error) { 1818da80660SBoris Popov FREE(xp, M_NULLFSNODE); 182df8bae1dSRodney W. Grimes return (error); 1832f9bae59SDavid Greenman } 184df8bae1dSRodney W. Grimes vp = *vpp; 185df8bae1dSRodney W. Grimes 186df8bae1dSRodney W. Grimes vp->v_type = lowervp->v_type; 187df8bae1dSRodney W. Grimes xp->null_vnode = vp; 188df8bae1dSRodney W. Grimes vp->v_data = xp; 189df8bae1dSRodney W. Grimes xp->null_lowervp = lowervp; 190df8bae1dSRodney W. Grimes /* 191df8bae1dSRodney W. Grimes * Before we insert our new node onto the hash chains, 192df8bae1dSRodney W. Grimes * check to see if someone else has beaten us to it. 193df8bae1dSRodney W. Grimes * (We could have slept in MALLOC.) 194df8bae1dSRodney W. Grimes */ 1952e52c1f9SBruce Evans othervp = null_node_find(mp, lowervp); 196623ae52eSPoul-Henning Kamp if (othervp) { 1974451405fSBoris Popov vp->v_data = NULL; 1988da80660SBoris Popov FREE(xp, M_NULLFSNODE); 199df8bae1dSRodney W. Grimes vp->v_type = VBAD; /* node is discarded */ 2004451405fSBoris Popov vrele(vp); 201df8bae1dSRodney W. Grimes *vpp = othervp; 202df8bae1dSRodney W. Grimes return 0; 203df8bae1dSRodney W. Grimes }; 2044451405fSBoris Popov 2054451405fSBoris Popov /* 2064451405fSBoris Popov * From NetBSD: 2074451405fSBoris Popov * Now lock the new node. We rely on the fact that we were passed 2084451405fSBoris Popov * a locked vnode. If the lower node is exporting a struct lock 2094451405fSBoris Popov * (v_vnlock != NULL) then we just set the upper v_vnlock to the 2104451405fSBoris Popov * lower one, and both are now locked. If the lower node is exporting 2114451405fSBoris Popov * NULL, then we copy that up and manually lock the new vnode. 2124451405fSBoris Popov */ 2134451405fSBoris Popov 2148da80660SBoris Popov lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p); 2154451405fSBoris Popov vp->v_vnlock = lowervp->v_vnlock; 2164451405fSBoris Popov error = VOP_LOCK(vp, LK_EXCLUSIVE | LK_THISLAYER, p); 2174451405fSBoris Popov if (error) 2184451405fSBoris Popov panic("null_node_alloc: can't lock new vnode\n"); 2194451405fSBoris Popov 2204451405fSBoris Popov VREF(lowervp); 221996c772fSJohn Dyson hd = NULL_NHASH(lowervp); 222996c772fSJohn Dyson LIST_INSERT_HEAD(hd, xp, null_hash); 2238da80660SBoris Popov lockmgr(&null_hashlock, LK_RELEASE, NULL, p); 224df8bae1dSRodney W. Grimes return 0; 225df8bae1dSRodney W. Grimes } 226df8bae1dSRodney W. Grimes 227df8bae1dSRodney W. Grimes 228df8bae1dSRodney W. Grimes /* 2294451405fSBoris Popov * Try to find an existing null_node vnode refering to the given underlying 2304451405fSBoris Popov * vnode (which should be locked). If no vnode found, create a new null_node 2314451405fSBoris Popov * vnode which contains a reference to the lower vnode. 232df8bae1dSRodney W. Grimes */ 233df8bae1dSRodney W. Grimes int 234df8bae1dSRodney W. Grimes null_node_create(mp, lowervp, newvpp) 235df8bae1dSRodney W. Grimes struct mount *mp; 236df8bae1dSRodney W. Grimes struct vnode *lowervp; 237df8bae1dSRodney W. Grimes struct vnode **newvpp; 238df8bae1dSRodney W. Grimes { 239df8bae1dSRodney W. Grimes struct vnode *aliasvp; 240df8bae1dSRodney W. Grimes 241623ae52eSPoul-Henning Kamp aliasvp = null_node_find(mp, lowervp); 242623ae52eSPoul-Henning Kamp if (aliasvp) { 243df8bae1dSRodney W. Grimes /* 244df8bae1dSRodney W. Grimes * null_node_find has taken another reference 245df8bae1dSRodney W. Grimes * to the alias vnode. 246df8bae1dSRodney W. Grimes */ 2474451405fSBoris Popov vrele(lowervp); 2488da80660SBoris Popov #ifdef NULLFS_DEBUG 249e958d078SKATO Takenori vprint("null_node_create: exists", aliasvp); 250df8bae1dSRodney W. Grimes #endif 251df8bae1dSRodney W. Grimes } else { 252df8bae1dSRodney W. Grimes int error; 253df8bae1dSRodney W. Grimes 254df8bae1dSRodney W. Grimes /* 255df8bae1dSRodney W. Grimes * Get new vnode. 256df8bae1dSRodney W. Grimes */ 2578da80660SBoris Popov NULLFSDEBUG("null_node_create: create new alias vnode\n"); 258df8bae1dSRodney W. Grimes 259df8bae1dSRodney W. Grimes /* 260df8bae1dSRodney W. Grimes * Make new vnode reference the null_node. 261df8bae1dSRodney W. Grimes */ 262623ae52eSPoul-Henning Kamp error = null_node_alloc(mp, lowervp, &aliasvp); 263623ae52eSPoul-Henning Kamp if (error) 264df8bae1dSRodney W. Grimes return error; 265df8bae1dSRodney W. Grimes 266df8bae1dSRodney W. Grimes /* 267df8bae1dSRodney W. Grimes * aliasvp is already VREF'd by getnewvnode() 268df8bae1dSRodney W. Grimes */ 269df8bae1dSRodney W. Grimes } 270df8bae1dSRodney W. Grimes 271df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC 272df8bae1dSRodney W. Grimes if (lowervp->v_usecount < 1) { 273df8bae1dSRodney W. Grimes /* Should never happen... */ 274623ae52eSPoul-Henning Kamp vprint ("null_node_create: alias ", aliasvp); 275623ae52eSPoul-Henning Kamp vprint ("null_node_create: lower ", lowervp); 276df8bae1dSRodney W. Grimes panic ("null_node_create: lower has 0 usecount."); 277df8bae1dSRodney W. Grimes }; 278df8bae1dSRodney W. Grimes #endif 279df8bae1dSRodney W. Grimes 2808da80660SBoris Popov #ifdef NULLFS_DEBUG 281df8bae1dSRodney W. Grimes vprint("null_node_create: alias", aliasvp); 282df8bae1dSRodney W. Grimes vprint("null_node_create: lower", lowervp); 283df8bae1dSRodney W. Grimes #endif 284df8bae1dSRodney W. Grimes 285df8bae1dSRodney W. Grimes *newvpp = aliasvp; 286df8bae1dSRodney W. Grimes return (0); 287df8bae1dSRodney W. Grimes } 288e958d078SKATO Takenori 289a0f40f54SBruce Evans #ifdef DIAGNOSTIC 2901bf978ceSKATO Takenori #include "opt_ddb.h" 2911bf978ceSKATO Takenori 292e958d078SKATO Takenori #ifdef DDB 293e958d078SKATO Takenori #define null_checkvp_barrier 1 294e958d078SKATO Takenori #else 295e958d078SKATO Takenori #define null_checkvp_barrier 0 296e958d078SKATO Takenori #endif 297e958d078SKATO Takenori 298df8bae1dSRodney W. Grimes struct vnode * 299df8bae1dSRodney W. Grimes null_checkvp(vp, fil, lno) 300df8bae1dSRodney W. Grimes struct vnode *vp; 301df8bae1dSRodney W. Grimes char *fil; 302df8bae1dSRodney W. Grimes int lno; 303df8bae1dSRodney W. Grimes { 304df8bae1dSRodney W. Grimes struct null_node *a = VTONULL(vp); 305df8bae1dSRodney W. Grimes #ifdef notyet 306df8bae1dSRodney W. Grimes /* 307df8bae1dSRodney W. Grimes * Can't do this check because vop_reclaim runs 308df8bae1dSRodney W. Grimes * with a funny vop vector. 309df8bae1dSRodney W. Grimes */ 310df8bae1dSRodney W. Grimes if (vp->v_op != null_vnodeop_p) { 311df8bae1dSRodney W. Grimes printf ("null_checkvp: on non-null-node\n"); 312df8bae1dSRodney W. Grimes while (null_checkvp_barrier) /*WAIT*/ ; 313df8bae1dSRodney W. Grimes panic("null_checkvp"); 314df8bae1dSRodney W. Grimes }; 315df8bae1dSRodney W. Grimes #endif 316c5e17d9eSKATO Takenori if (a->null_lowervp == NULLVP) { 317df8bae1dSRodney W. Grimes /* Should never happen */ 318df8bae1dSRodney W. Grimes int i; u_long *p; 31989785a16SBruce Evans printf("vp = %p, ZERO ptr\n", (void *)vp); 320df8bae1dSRodney W. Grimes for (p = (u_long *) a, i = 0; i < 8; i++) 32189785a16SBruce Evans printf(" %lx", p[i]); 322df8bae1dSRodney W. Grimes printf("\n"); 323df8bae1dSRodney W. Grimes /* wait for debugger */ 324df8bae1dSRodney W. Grimes while (null_checkvp_barrier) /*WAIT*/ ; 325df8bae1dSRodney W. Grimes panic("null_checkvp"); 326df8bae1dSRodney W. Grimes } 327df8bae1dSRodney W. Grimes if (a->null_lowervp->v_usecount < 1) { 328df8bae1dSRodney W. Grimes int i; u_long *p; 32989785a16SBruce Evans printf("vp = %p, unref'ed lowervp\n", (void *)vp); 330df8bae1dSRodney W. Grimes for (p = (u_long *) a, i = 0; i < 8; i++) 33189785a16SBruce Evans printf(" %lx", p[i]); 332df8bae1dSRodney W. Grimes printf("\n"); 333df8bae1dSRodney W. Grimes /* wait for debugger */ 334df8bae1dSRodney W. Grimes while (null_checkvp_barrier) /*WAIT*/ ; 335df8bae1dSRodney W. Grimes panic ("null with unref'ed lowervp"); 336df8bae1dSRodney W. Grimes }; 337df8bae1dSRodney W. Grimes #ifdef notyet 338df8bae1dSRodney W. Grimes printf("null %x/%d -> %x/%d [%s, %d]\n", 339df8bae1dSRodney W. Grimes NULLTOV(a), NULLTOV(a)->v_usecount, 340df8bae1dSRodney W. Grimes a->null_lowervp, a->null_lowervp->v_usecount, 341df8bae1dSRodney W. Grimes fil, lno); 342df8bae1dSRodney W. Grimes #endif 343df8bae1dSRodney W. Grimes return a->null_lowervp; 344df8bae1dSRodney W. Grimes } 345df8bae1dSRodney W. Grimes #endif 346