1 /* 2 * Copyright (c) 1992, 1993 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software donated to Berkeley by 6 * Jan-Simon Pendry. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * @(#)null_subr.c 8.7 (Berkeley) 5/14/95 37 * 38 * $FreeBSD$ 39 */ 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kernel.h> 44 #include <sys/vnode.h> 45 #include <sys/mount.h> 46 #include <sys/malloc.h> 47 #include <miscfs/nullfs/null.h> 48 49 #define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */ 50 #define NNULLNODECACHE 16 51 52 /* 53 * Null layer cache: 54 * Each cache entry holds a reference to the lower vnode 55 * along with a pointer to the alias vnode. When an 56 * entry is added the lower vnode is VREF'd. When the 57 * alias is removed the lower vnode is vrele'd. 58 */ 59 60 #define NULL_NHASH(vp) \ 61 (&null_node_hashtbl[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & null_node_hash]) 62 63 static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl; 64 static u_long null_node_hash; 65 struct lock null_hashlock; 66 67 static MALLOC_DEFINE(M_NULLFSHASH, "NULLFS hash", "NULLFS hash table"); 68 MALLOC_DEFINE(M_NULLFSNODE, "NULLFS node", "NULLFS vnode private part"); 69 70 static int null_node_alloc(struct mount *mp, struct vnode *lowervp, 71 struct vnode **vpp); 72 static struct vnode * 73 null_node_find(struct mount *mp, struct vnode *lowervp); 74 75 /* 76 * Initialise cache headers 77 */ 78 int 79 nullfs_init(vfsp) 80 struct vfsconf *vfsp; 81 { 82 83 NULLFSDEBUG("nullfs_init\n"); /* printed during system boot */ 84 null_node_hashtbl = hashinit(NNULLNODECACHE, M_NULLFSHASH, &null_node_hash); 85 lockinit(&null_hashlock, PVFS, "nullhs", 0, 0); 86 return (0); 87 } 88 89 int 90 nullfs_uninit(vfsp) 91 struct vfsconf *vfsp; 92 { 93 94 if (null_node_hashtbl) { 95 lockdestroy(&null_hashlock); 96 free(null_node_hashtbl, M_NULLFSHASH); 97 } 98 return (0); 99 } 100 101 /* 102 * Return a VREF'ed alias for lower vnode if already exists, else 0. 103 * Lower vnode should be locked on entry and will be left locked on exit. 104 */ 105 static struct vnode * 106 null_node_find(mp, lowervp) 107 struct mount *mp; 108 struct vnode *lowervp; 109 { 110 struct proc *p = curproc; /* XXX */ 111 struct null_node_hashhead *hd; 112 struct null_node *a; 113 struct vnode *vp; 114 115 /* 116 * Find hash base, and then search the (two-way) linked 117 * list looking for a null_node structure which is referencing 118 * the lower vnode. If found, the increment the null_node 119 * reference count (but NOT the lower vnode's VREF counter). 120 */ 121 hd = NULL_NHASH(lowervp); 122 loop: 123 lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p); 124 for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) { 125 if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) { 126 vp = NULLTOV(a); 127 lockmgr(&null_hashlock, LK_RELEASE, NULL, p); 128 /* 129 * We need vget for the VXLOCK 130 * stuff, but we don't want to lock 131 * the lower node. 132 */ 133 if (vget(vp, LK_EXCLUSIVE | LK_CANRECURSE, p)) { 134 printf ("null_node_find: vget failed.\n"); 135 goto loop; 136 }; 137 /* 138 * Now we got both vnodes locked, so release the 139 * lower one. 140 */ 141 VOP_UNLOCK(lowervp, 0, p); 142 return (vp); 143 } 144 } 145 lockmgr(&null_hashlock, LK_RELEASE, NULL, p); 146 147 return NULLVP; 148 } 149 150 151 /* 152 * Make a new null_node node. 153 * Vp is the alias vnode, lofsvp is the lower vnode. 154 * Maintain a reference to (lowervp). 155 */ 156 static int 157 null_node_alloc(mp, lowervp, vpp) 158 struct mount *mp; 159 struct vnode *lowervp; 160 struct vnode **vpp; 161 { 162 struct proc *p = curproc; /* XXX */ 163 struct null_node_hashhead *hd; 164 struct null_node *xp; 165 struct vnode *othervp, *vp; 166 int error; 167 168 /* 169 * Do the MALLOC before the getnewvnode since doing so afterward 170 * might cause a bogus v_data pointer to get dereferenced 171 * elsewhere if MALLOC should block. 172 */ 173 MALLOC(xp, struct null_node *, sizeof(struct null_node), 174 M_NULLFSNODE, M_WAITOK); 175 176 error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp); 177 if (error) { 178 FREE(xp, M_NULLFSNODE); 179 return (error); 180 } 181 vp = *vpp; 182 183 vp->v_type = lowervp->v_type; 184 xp->null_vnode = vp; 185 vp->v_data = xp; 186 xp->null_lowervp = lowervp; 187 /* 188 * Before we insert our new node onto the hash chains, 189 * check to see if someone else has beaten us to it. 190 * (We could have slept in MALLOC.) 191 */ 192 othervp = null_node_find(mp, lowervp); 193 if (othervp) { 194 vp->v_data = NULL; 195 FREE(xp, M_NULLFSNODE); 196 vp->v_type = VBAD; /* node is discarded */ 197 vrele(vp); 198 *vpp = othervp; 199 return 0; 200 }; 201 202 /* 203 * From NetBSD: 204 * Now lock the new node. We rely on the fact that we were passed 205 * a locked vnode. If the lower node is exporting a struct lock 206 * (v_vnlock != NULL) then we just set the upper v_vnlock to the 207 * lower one, and both are now locked. If the lower node is exporting 208 * NULL, then we copy that up and manually lock the new vnode. 209 */ 210 211 lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p); 212 vp->v_vnlock = lowervp->v_vnlock; 213 error = VOP_LOCK(vp, LK_EXCLUSIVE | LK_THISLAYER, p); 214 if (error) 215 panic("null_node_alloc: can't lock new vnode\n"); 216 217 VREF(lowervp); 218 hd = NULL_NHASH(lowervp); 219 LIST_INSERT_HEAD(hd, xp, null_hash); 220 lockmgr(&null_hashlock, LK_RELEASE, NULL, p); 221 return 0; 222 } 223 224 225 /* 226 * Try to find an existing null_node vnode refering to the given underlying 227 * vnode (which should be locked). If no vnode found, create a new null_node 228 * vnode which contains a reference to the lower vnode. 229 */ 230 int 231 null_node_create(mp, lowervp, newvpp) 232 struct mount *mp; 233 struct vnode *lowervp; 234 struct vnode **newvpp; 235 { 236 struct vnode *aliasvp; 237 238 aliasvp = null_node_find(mp, lowervp); 239 if (aliasvp) { 240 /* 241 * null_node_find has taken another reference 242 * to the alias vnode. 243 */ 244 vrele(lowervp); 245 #ifdef NULLFS_DEBUG 246 vprint("null_node_create: exists", aliasvp); 247 #endif 248 } else { 249 int error; 250 251 /* 252 * Get new vnode. 253 */ 254 NULLFSDEBUG("null_node_create: create new alias vnode\n"); 255 256 /* 257 * Make new vnode reference the null_node. 258 */ 259 error = null_node_alloc(mp, lowervp, &aliasvp); 260 if (error) 261 return error; 262 263 /* 264 * aliasvp is already VREF'd by getnewvnode() 265 */ 266 } 267 268 #ifdef DIAGNOSTIC 269 if (lowervp->v_usecount < 1) { 270 /* Should never happen... */ 271 vprint ("null_node_create: alias ", aliasvp); 272 vprint ("null_node_create: lower ", lowervp); 273 panic ("null_node_create: lower has 0 usecount."); 274 }; 275 #endif 276 277 #ifdef NULLFS_DEBUG 278 vprint("null_node_create: alias", aliasvp); 279 vprint("null_node_create: lower", lowervp); 280 #endif 281 282 *newvpp = aliasvp; 283 return (0); 284 } 285 286 #ifdef DIAGNOSTIC 287 #include "opt_ddb.h" 288 289 #ifdef DDB 290 #define null_checkvp_barrier 1 291 #else 292 #define null_checkvp_barrier 0 293 #endif 294 295 struct vnode * 296 null_checkvp(vp, fil, lno) 297 struct vnode *vp; 298 char *fil; 299 int lno; 300 { 301 struct null_node *a = VTONULL(vp); 302 #ifdef notyet 303 /* 304 * Can't do this check because vop_reclaim runs 305 * with a funny vop vector. 306 */ 307 if (vp->v_op != null_vnodeop_p) { 308 printf ("null_checkvp: on non-null-node\n"); 309 while (null_checkvp_barrier) /*WAIT*/ ; 310 panic("null_checkvp"); 311 }; 312 #endif 313 if (a->null_lowervp == NULLVP) { 314 /* Should never happen */ 315 int i; u_long *p; 316 printf("vp = %p, ZERO ptr\n", (void *)vp); 317 for (p = (u_long *) a, i = 0; i < 8; i++) 318 printf(" %lx", p[i]); 319 printf("\n"); 320 /* wait for debugger */ 321 while (null_checkvp_barrier) /*WAIT*/ ; 322 panic("null_checkvp"); 323 } 324 if (a->null_lowervp->v_usecount < 1) { 325 int i; u_long *p; 326 printf("vp = %p, unref'ed lowervp\n", (void *)vp); 327 for (p = (u_long *) a, i = 0; i < 8; i++) 328 printf(" %lx", p[i]); 329 printf("\n"); 330 /* wait for debugger */ 331 while (null_checkvp_barrier) /*WAIT*/ ; 332 panic ("null with unref'ed lowervp"); 333 }; 334 #ifdef notyet 335 printf("null %x/%d -> %x/%d [%s, %d]\n", 336 NULLTOV(a), NULLTOV(a)->v_usecount, 337 a->null_lowervp, a->null_lowervp->v_usecount, 338 fil, lno); 339 #endif 340 return a->null_lowervp; 341 } 342 #endif 343