1 /* 2 * Copyright (c) 1992 The Regents of the University of California 3 * Copyright (c) 1990, 1992 Jan-Simon Pendry 4 * All rights reserved. 5 * 6 * This code is derived from software donated to Berkeley by 7 * Jan-Simon Pendry. 8 * 9 * %sccs.include.redist.c% 10 * 11 * @(#)lofs_subr.c 1.2 (Berkeley) 6/18/92 12 * 13 * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $ 14 */ 15 16 #include <sys/param.h> 17 #include <sys/systm.h> 18 #include <sys/time.h> 19 #include <sys/types.h> 20 #include <sys/vnode.h> 21 #include <sys/mount.h> 22 #include <sys/namei.h> 23 #include <sys/malloc.h> 24 #include <nullfs/null.h> 25 26 #define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */ 27 #define NNULLNODECACHE 16 28 #define NULL_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NNULLNODECACHE-1)) 29 30 /* 31 * Null layer cache: 32 * Each cache entry holds a reference to the target vnode 33 * along with a pointer to the alias vnode. When an 34 * entry is added the target vnode is VREF'd. When the 35 * alias is removed the target vnode is vrele'd. 36 */ 37 38 /* 39 * Cache head 40 */ 41 struct null_node_cache { 42 struct null_node *ac_forw; 43 struct null_node *ac_back; 44 }; 45 46 static struct null_node_cache null_node_cache[NNULLNODECACHE]; 47 48 /* 49 * Initialise cache headers 50 */ 51 nullfs_init() 52 { 53 struct null_node_cache *ac; 54 #ifdef NULLFS_DIAGNOSTIC 55 printf("nullfs_init\n"); /* printed during system boot */ 56 #endif 57 58 for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++) 59 ac->ac_forw = ac->ac_back = (struct null_node *) ac; 60 } 61 62 /* 63 * Compute hash list for given target vnode 64 */ 65 static struct null_node_cache * 66 null_node_hash(targetvp) 67 struct vnode *targetvp; 68 { 69 return (&null_node_cache[NULL_NHASH(targetvp)]); 70 } 71 72 /* 73 * Make a new null_node node. 74 * Vp is the alias vnode, lofsvp is the target vnode. 75 * Maintain a reference to (targetvp). 76 */ 77 static void 78 null_node_alloc(vp, targetvp) 79 struct vnode *vp; 80 struct vnode *targetvp; 81 { 82 struct null_node_cache *hd; 83 struct null_node *a; 84 85 #ifdef NULLFS_DIAGNOSTIC 86 printf("null_node_alloc(%x, %x)\n", vp, targetvp); 87 #endif 88 89 MALLOC(a, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK); 90 a->null_vnode = vp; 91 vp->v_data = a; 92 VREF(targetvp); 93 a->null_lowervp = targetvp; 94 hd = null_node_hash(targetvp); 95 insque(a, hd); 96 97 #ifdef NULLFS_DIAGNOSTIC 98 vprint("alloc vp", vp); 99 vprint("alloc targetvp", targetvp); 100 #endif 101 } 102 103 #ifdef NULLFS_DIAGNOSTIC 104 /* 105 * NEEDSWORK: The ability to set lowervp to null here 106 * implies that one can never count on lowervp staying null 107 * (even if vp is locked). This seems quite bad. Think 108 * about these things. 109 */ 110 void 111 null_node_flushmp (mp) 112 struct mount *mp; 113 { 114 struct null_node_cache *ac; 115 int i = 0; 116 struct null_node *roota; 117 118 printf("null_node_flushmp (%x)\n", mp); 119 120 roota = VTONULLNODE(MOUNTTONULLMOUNT(mp)->nullm_rootvp); 121 122 for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++) { 123 struct null_node *a = ac->ac_forw; 124 while (a != (struct null_node *) ac) { 125 if (a != roota && a->null_vnode->v_mount == mp) { 126 struct vnode *vp = a->null_lowervp; 127 if (vp) { 128 a->null_lowervp = 0; 129 vprint("would vrele", vp); 130 /*vrele(vp);*/ 131 i++; 132 } 133 } 134 a = a->null_forw; 135 } 136 } 137 if (i > 0) 138 printf("null_node: vrele'd %d aliases\n", i); 139 } 140 #endif 141 142 /* 143 * Return alias for target vnode if already exists, else 0. 144 */ 145 static struct null_node * 146 null_node_find(mp, targetvp) 147 struct mount *mp; 148 struct vnode *targetvp; 149 { 150 struct null_node_cache *hd; 151 struct null_node *a; 152 153 #ifdef NULLFS_DIAGNOSTIC 154 printf("null_node_find(mp = %x, target = %x)\n", mp, targetvp); 155 #endif 156 157 /* 158 * Find hash base, and then search the (two-way) linked 159 * list looking for a null_node structure which is referencing 160 * the target vnode. If found, the increment the null_node 161 * reference count (but NOT the target vnode's VREF counter). 162 */ 163 hd = null_node_hash(targetvp); 164 165 for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) { 166 if (a->null_lowervp == targetvp && a->null_vnode->v_mount == mp) { 167 #ifdef NULLFS_DIAGNOSTIC 168 printf("null_node_find(%x): found (%x,%x)->%x\n", 169 targetvp, mp, a->null_vnode, targetvp); 170 #endif 171 return (a); 172 } 173 } 174 175 #ifdef NULLFS_DIAGNOSTIC 176 printf("null_node_find(%x, %x): NOT found\n", mp, targetvp); 177 #endif 178 179 return (0); 180 } 181 182 /* 183 * Try to find an existing null_node vnode refering 184 * to it, otherwise make a new null_node vnode which 185 * contains a reference to the target vnode. 186 */ 187 int 188 null_node_create(mp, targetvp, newvpp) 189 struct mount *mp; 190 struct vnode *targetvp; 191 struct vnode **newvpp; 192 { 193 struct null_node *ap; 194 struct vnode *aliasvp; 195 196 if (targetvp->v_type != VDIR || targetvp->v_op == null_vnodeop_p) { 197 *newvpp = targetvp; 198 return; 199 } 200 201 ap = null_node_find(mp, targetvp); 202 203 if (ap) { 204 /* 205 * Take another reference to the alias vnode 206 */ 207 #ifdef NULLFS_DIAGNOSTIC 208 vprint("null_node_alias: exists", ap->null_vnode); 209 #endif 210 aliasvp = ap->null_vnode; 211 VREF(aliasvp); 212 } else { 213 int error; 214 215 /* 216 * Get new vnode. 217 */ 218 #ifdef NULLFS_DIAGNOSTIC 219 printf("null_node_alias: create new alias vnode\n"); 220 #endif 221 if (error = getnewvnode(VT_UFS, mp, null_vnodeop_p, &aliasvp)) 222 return (error); /* XXX: VT_LOFS above */ 223 224 /* 225 * Must be a directory 226 */ 227 aliasvp->v_type = VDIR; 228 229 /* 230 * Make new vnode reference the null_node. 231 */ 232 null_node_alloc(aliasvp, targetvp); 233 234 /* 235 * aliasvp is already VREF'd by getnewvnode() 236 */ 237 } 238 239 vrele(targetvp); 240 241 #ifdef NULLFS_DIAGNOSTIC 242 vprint("null_node_alias alias", aliasvp); 243 vprint("null_node_alias target", targetvp); 244 #endif 245 246 *newvpp = aliasvp; 247 return (0); 248 } 249 #ifdef NULLFS_DIAGNOSTIC 250 struct vnode * 251 null_checkvp(vp, fil, lno) 252 struct vnode *vp; 253 char *fil; 254 int lno; 255 { 256 struct null_node *a = VTONULLNODE(vp); 257 if (a->null_lowervp == 0) { 258 /* Should never happen */ 259 int i; u_long *p; 260 printf("vp = %x, ZERO ptr\n", vp); 261 for (p = (u_long *) a, i = 0; i < 8; i++) 262 printf(" %x", p[i]); 263 printf("\n"); 264 DELAY(2000000); 265 panic("null_checkvp"); 266 } 267 printf("nullvp %x/%d -> %x/%d [%s, %d]\n", 268 a->null_vnode, a->null_vnode->v_usecount, 269 a->null_lowervp, a->null_lowervp->v_usecount, 270 fil, lno); 271 return a->null_lowervp; 272 } 273 #endif 274