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