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/lock.h> 45 #include <sys/malloc.h> 46 #include <sys/mount.h> 47 #include <sys/proc.h> 48 #include <sys/vnode.h> 49 50 #include <fs/nullfs/null.h> 51 52 #define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */ 53 #define NNULLNODECACHE 16 54 55 /* 56 * Null layer cache: 57 * Each cache entry holds a reference to the lower vnode 58 * along with a pointer to the alias vnode. When an 59 * entry is added the lower vnode is VREF'd. When the 60 * alias is removed the lower vnode is vrele'd. 61 */ 62 63 #define NULL_NHASH(vp) \ 64 (&null_node_hashtbl[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & null_node_hash]) 65 66 static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl; 67 static u_long null_node_hash; 68 struct lock null_hashlock; 69 70 static MALLOC_DEFINE(M_NULLFSHASH, "NULLFS hash", "NULLFS hash table"); 71 MALLOC_DEFINE(M_NULLFSNODE, "NULLFS node", "NULLFS vnode private part"); 72 73 static int null_node_alloc(struct mount *mp, struct vnode *lowervp, 74 struct vnode **vpp); 75 static struct vnode * 76 null_node_find(struct mount *mp, struct vnode *lowervp); 77 78 /* 79 * Initialise cache headers 80 */ 81 int 82 nullfs_init(vfsp) 83 struct vfsconf *vfsp; 84 { 85 86 NULLFSDEBUG("nullfs_init\n"); /* printed during system boot */ 87 null_node_hashtbl = hashinit(NNULLNODECACHE, M_NULLFSHASH, &null_node_hash); 88 lockinit(&null_hashlock, PVFS, "nullhs", 0, 0); 89 return (0); 90 } 91 92 int 93 nullfs_uninit(vfsp) 94 struct vfsconf *vfsp; 95 { 96 97 if (null_node_hashtbl) { 98 lockdestroy(&null_hashlock); 99 free(null_node_hashtbl, M_NULLFSHASH); 100 } 101 return (0); 102 } 103 104 /* 105 * Return a VREF'ed alias for lower vnode if already exists, else 0. 106 * Lower vnode should be locked on entry and will be left locked on exit. 107 */ 108 static struct vnode * 109 null_node_find(mp, lowervp) 110 struct mount *mp; 111 struct vnode *lowervp; 112 { 113 struct thread *td = curthread; /* XXX */ 114 struct null_node_hashhead *hd; 115 struct null_node *a; 116 struct vnode *vp; 117 118 /* 119 * Find hash base, and then search the (two-way) linked 120 * list looking for a null_node structure which is referencing 121 * the lower vnode. If found, the increment the null_node 122 * reference count (but NOT the lower vnode's VREF counter). 123 */ 124 hd = NULL_NHASH(lowervp); 125 loop: 126 lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, td); 127 LIST_FOREACH(a, hd, null_hash) { 128 if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) { 129 vp = NULLTOV(a); 130 lockmgr(&null_hashlock, LK_RELEASE, NULL, td); 131 /* 132 * We need vget for the VXLOCK 133 * stuff, but we don't want to lock 134 * the lower node. 135 */ 136 if (vget(vp, LK_EXCLUSIVE | LK_CANRECURSE, td)) { 137 printf ("null_node_find: vget failed.\n"); 138 goto loop; 139 }; 140 /* 141 * Now we got both vnodes locked, so release the 142 * lower one. 143 */ 144 VOP_UNLOCK(lowervp, 0, td); 145 return (vp); 146 } 147 } 148 lockmgr(&null_hashlock, LK_RELEASE, NULL, td); 149 150 return NULLVP; 151 } 152 153 154 /* 155 * Make a new null_node node. 156 * Vp is the alias vnode, lofsvp is the lower vnode. 157 * Maintain a reference to (lowervp). 158 */ 159 static int 160 null_node_alloc(mp, lowervp, vpp) 161 struct mount *mp; 162 struct vnode *lowervp; 163 struct vnode **vpp; 164 { 165 struct thread *td = curthread; /* XXX */ 166 struct null_node_hashhead *hd; 167 struct null_node *xp; 168 struct vnode *othervp, *vp; 169 int error; 170 171 /* 172 * Do the MALLOC before the getnewvnode since doing so afterward 173 * might cause a bogus v_data pointer to get dereferenced 174 * elsewhere if MALLOC should block. 175 */ 176 MALLOC(xp, struct null_node *, sizeof(struct null_node), 177 M_NULLFSNODE, M_WAITOK); 178 179 error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp); 180 if (error) { 181 FREE(xp, M_NULLFSNODE); 182 return (error); 183 } 184 vp = *vpp; 185 186 vp->v_type = lowervp->v_type; 187 xp->null_vnode = vp; 188 vp->v_data = xp; 189 xp->null_lowervp = lowervp; 190 /* 191 * Before we insert our new node onto the hash chains, 192 * check to see if someone else has beaten us to it. 193 * (We could have slept in MALLOC.) 194 */ 195 othervp = null_node_find(mp, lowervp); 196 if (othervp) { 197 vp->v_data = NULL; 198 FREE(xp, M_NULLFSNODE); 199 vp->v_type = VBAD; /* node is discarded */ 200 vrele(vp); 201 *vpp = othervp; 202 return 0; 203 }; 204 205 /* 206 * From NetBSD: 207 * Now lock the new node. We rely on the fact that we were passed 208 * a locked vnode. If the lower node is exporting a struct lock 209 * (v_vnlock != NULL) then we just set the upper v_vnlock to the 210 * lower one, and both are now locked. If the lower node is exporting 211 * NULL, then we copy that up and manually lock the new vnode. 212 */ 213 214 lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, td); 215 vp->v_vnlock = lowervp->v_vnlock; 216 error = VOP_LOCK(vp, LK_EXCLUSIVE | LK_THISLAYER, td); 217 if (error) 218 panic("null_node_alloc: can't lock new vnode\n"); 219 220 VREF(lowervp); 221 hd = NULL_NHASH(lowervp); 222 LIST_INSERT_HEAD(hd, xp, null_hash); 223 lockmgr(&null_hashlock, LK_RELEASE, NULL, td); 224 return 0; 225 } 226 227 228 /* 229 * Try to find an existing null_node vnode refering to the given underlying 230 * vnode (which should be locked). If no vnode found, create a new null_node 231 * vnode which contains a reference to the lower vnode. 232 */ 233 int 234 null_node_create(mp, lowervp, newvpp) 235 struct mount *mp; 236 struct vnode *lowervp; 237 struct vnode **newvpp; 238 { 239 struct vnode *aliasvp; 240 241 aliasvp = null_node_find(mp, lowervp); 242 if (aliasvp) { 243 /* 244 * null_node_find has taken another reference 245 * to the alias vnode. 246 */ 247 vrele(lowervp); 248 #ifdef NULLFS_DEBUG 249 vprint("null_node_create: exists", aliasvp); 250 #endif 251 } else { 252 int error; 253 254 /* 255 * Get new vnode. 256 */ 257 NULLFSDEBUG("null_node_create: create new alias vnode\n"); 258 259 /* 260 * Make new vnode reference the null_node. 261 */ 262 error = null_node_alloc(mp, lowervp, &aliasvp); 263 if (error) 264 return error; 265 266 /* 267 * aliasvp is already VREF'd by getnewvnode() 268 */ 269 } 270 271 #ifdef DIAGNOSTIC 272 if (lowervp->v_usecount < 1) { 273 /* Should never happen... */ 274 vprint ("null_node_create: alias ", aliasvp); 275 vprint ("null_node_create: lower ", lowervp); 276 panic ("null_node_create: lower has 0 usecount."); 277 }; 278 #endif 279 280 #ifdef NULLFS_DEBUG 281 vprint("null_node_create: alias", aliasvp); 282 vprint("null_node_create: lower", lowervp); 283 #endif 284 285 *newvpp = aliasvp; 286 return (0); 287 } 288 289 #ifdef DIAGNOSTIC 290 #include "opt_ddb.h" 291 292 #ifdef DDB 293 #define null_checkvp_barrier 1 294 #else 295 #define null_checkvp_barrier 0 296 #endif 297 298 struct vnode * 299 null_checkvp(vp, fil, lno) 300 struct vnode *vp; 301 char *fil; 302 int lno; 303 { 304 struct null_node *a = VTONULL(vp); 305 #ifdef notyet 306 /* 307 * Can't do this check because vop_reclaim runs 308 * with a funny vop vector. 309 */ 310 if (vp->v_op != null_vnodeop_p) { 311 printf ("null_checkvp: on non-null-node\n"); 312 while (null_checkvp_barrier) /*WAIT*/ ; 313 panic("null_checkvp"); 314 }; 315 #endif 316 if (a->null_lowervp == NULLVP) { 317 /* Should never happen */ 318 int i; u_long *p; 319 printf("vp = %p, ZERO ptr\n", (void *)vp); 320 for (p = (u_long *) a, i = 0; i < 8; i++) 321 printf(" %lx", p[i]); 322 printf("\n"); 323 /* wait for debugger */ 324 while (null_checkvp_barrier) /*WAIT*/ ; 325 panic("null_checkvp"); 326 } 327 if (a->null_lowervp->v_usecount < 1) { 328 int i; u_long *p; 329 printf("vp = %p, unref'ed lowervp\n", (void *)vp); 330 for (p = (u_long *) a, i = 0; i < 8; i++) 331 printf(" %lx", p[i]); 332 printf("\n"); 333 /* wait for debugger */ 334 while (null_checkvp_barrier) /*WAIT*/ ; 335 panic ("null with unref'ed lowervp"); 336 }; 337 #ifdef notyet 338 printf("null %x/%d -> %x/%d [%s, %d]\n", 339 NULLTOV(a), NULLTOV(a)->v_usecount, 340 a->null_lowervp, a->null_lowervp->v_usecount, 341 fil, lno); 342 #endif 343 return a->null_lowervp; 344 } 345 #endif 346