1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software donated to Berkeley by 8 * Jan-Simon Pendry. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 * @(#)null_subr.c 8.7 (Berkeley) 5/14/95 35 */ 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/lock.h> 41 #include <sys/rwlock.h> 42 #include <sys/malloc.h> 43 #include <sys/mount.h> 44 #include <sys/proc.h> 45 #include <sys/vnode.h> 46 47 #include <fs/nullfs/null.h> 48 49 /* 50 * Null layer cache: 51 * Each cache entry holds a reference to the lower vnode 52 * along with a pointer to the alias vnode. When an 53 * entry is added the lower vnode is VREF'd. When the 54 * alias is removed the lower vnode is vrele'd. 55 */ 56 57 #define NULL_NHASH(vp) (&null_node_hashtbl[vfs_hash_index(vp) & null_hash_mask]) 58 59 static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl; 60 static struct rwlock null_hash_lock; 61 static u_long null_hash_mask; 62 63 static MALLOC_DEFINE(M_NULLFSHASH, "nullfs_hash", "NULLFS hash table"); 64 MALLOC_DEFINE(M_NULLFSNODE, "nullfs_node", "NULLFS vnode private part"); 65 66 static void null_hashins(struct mount *, struct null_node *); 67 68 /* 69 * Initialise cache headers 70 */ 71 int 72 nullfs_init(struct vfsconf *vfsp) 73 { 74 75 null_node_hashtbl = hashinit(desiredvnodes, M_NULLFSHASH, 76 &null_hash_mask); 77 rw_init(&null_hash_lock, "nullhs"); 78 return (0); 79 } 80 81 int 82 nullfs_uninit(struct vfsconf *vfsp) 83 { 84 85 rw_destroy(&null_hash_lock); 86 hashdestroy(null_node_hashtbl, M_NULLFSHASH, null_hash_mask); 87 return (0); 88 } 89 90 /* 91 * Return a VREF'ed alias for lower vnode if already exists, else 0. 92 * Lower vnode should be locked on entry and will be left locked on exit. 93 */ 94 static struct vnode * 95 null_hashget_locked(struct mount *mp, struct vnode *lowervp) 96 { 97 struct null_node_hashhead *hd; 98 struct null_node *a; 99 struct vnode *vp; 100 101 ASSERT_VOP_LOCKED(lowervp, "null_hashget"); 102 rw_assert(&null_hash_lock, RA_LOCKED); 103 104 /* 105 * Find hash base, and then search the (two-way) linked 106 * list looking for a null_node structure which is referencing 107 * the lower vnode. If found, the increment the null_node 108 * reference count (but NOT the lower vnode's VREF counter). 109 */ 110 hd = NULL_NHASH(lowervp); 111 LIST_FOREACH(a, hd, null_hash) { 112 if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) { 113 /* 114 * Since we have the lower node locked the nullfs 115 * node can not be in the process of recycling. If 116 * it had been recycled before we grabed the lower 117 * lock it would not have been found on the hash. 118 */ 119 vp = NULLTOV(a); 120 vref(vp); 121 return (vp); 122 } 123 } 124 return (NULLVP); 125 } 126 127 struct vnode * 128 null_hashget(struct mount *mp, struct vnode *lowervp) 129 { 130 struct null_node_hashhead *hd; 131 struct vnode *vp; 132 133 hd = NULL_NHASH(lowervp); 134 if (LIST_EMPTY(hd)) 135 return (NULLVP); 136 137 rw_rlock(&null_hash_lock); 138 vp = null_hashget_locked(mp, lowervp); 139 rw_runlock(&null_hash_lock); 140 141 return (vp); 142 } 143 144 static void 145 null_hashins(struct mount *mp, struct null_node *xp) 146 { 147 struct null_node_hashhead *hd; 148 #ifdef INVARIANTS 149 struct null_node *oxp; 150 #endif 151 152 rw_assert(&null_hash_lock, RA_WLOCKED); 153 154 hd = NULL_NHASH(xp->null_lowervp); 155 #ifdef INVARIANTS 156 LIST_FOREACH(oxp, hd, null_hash) { 157 if (oxp->null_lowervp == xp->null_lowervp && 158 NULLTOV(oxp)->v_mount == mp) { 159 VNASSERT(0, NULLTOV(oxp), 160 ("vnode already in hash")); 161 } 162 } 163 #endif 164 LIST_INSERT_HEAD(hd, xp, null_hash); 165 } 166 167 static void 168 null_destroy_proto(struct vnode *vp, void *xp) 169 { 170 171 lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL); 172 VI_LOCK(vp); 173 vp->v_data = NULL; 174 vp->v_vnlock = &vp->v_lock; 175 vp->v_op = &dead_vnodeops; 176 VI_UNLOCK(vp); 177 vgone(vp); 178 vput(vp); 179 free(xp, M_NULLFSNODE); 180 } 181 182 /* 183 * Make a new or get existing nullfs node. 184 * Vp is the alias vnode, lowervp is the lower vnode. 185 * 186 * The lowervp assumed to be locked and having "spare" reference. This routine 187 * vrele lowervp if nullfs node was taken from hash. Otherwise it "transfers" 188 * the caller's "spare" reference to created nullfs vnode. 189 */ 190 int 191 null_nodeget(struct mount *mp, struct vnode *lowervp, struct vnode **vpp) 192 { 193 struct null_node *xp; 194 struct vnode *vp; 195 int error; 196 197 ASSERT_VOP_LOCKED(lowervp, "lowervp"); 198 VNPASS(lowervp->v_usecount > 0, lowervp); 199 200 /* Lookup the hash firstly. */ 201 *vpp = null_hashget(mp, lowervp); 202 if (*vpp != NULL) { 203 vrele(lowervp); 204 return (0); 205 } 206 207 /* 208 * We do not serialize vnode creation, instead we will check for 209 * duplicates later, when adding new vnode to hash. 210 * Note that duplicate can only appear in hash if the lowervp is 211 * locked LK_SHARED. 212 */ 213 xp = malloc(sizeof(struct null_node), M_NULLFSNODE, M_WAITOK); 214 215 error = getnewvnode("nullfs", mp, &null_vnodeops, &vp); 216 if (error) { 217 vput(lowervp); 218 free(xp, M_NULLFSNODE); 219 return (error); 220 } 221 222 VNPASS(vp->v_object == NULL, vp); 223 VNPASS((vn_irflag_read(vp) & VIRF_PGREAD) == 0, vp); 224 225 rw_wlock(&null_hash_lock); 226 xp->null_vnode = vp; 227 xp->null_lowervp = lowervp; 228 xp->null_flags = 0; 229 vp->v_type = lowervp->v_type; 230 vp->v_data = xp; 231 vp->v_vnlock = lowervp->v_vnlock; 232 *vpp = null_hashget_locked(mp, lowervp); 233 if (*vpp != NULL) { 234 rw_wunlock(&null_hash_lock); 235 vrele(lowervp); 236 null_destroy_proto(vp, xp); 237 return (0); 238 } 239 240 /* 241 * We might miss the case where lower vnode sets VIRF_PGREAD 242 * some time after construction, which is typical case. 243 * null_open rechecks. 244 */ 245 if ((vn_irflag_read(lowervp) & VIRF_PGREAD) != 0) { 246 MPASS(lowervp->v_object != NULL); 247 vp->v_object = lowervp->v_object; 248 vn_irflag_set(vp, VIRF_PGREAD); 249 } 250 if (lowervp == MOUNTTONULLMOUNT(mp)->nullm_lowerrootvp) 251 vp->v_vflag |= VV_ROOT; 252 253 error = insmntque1(vp, mp); 254 if (error != 0) { 255 rw_wunlock(&null_hash_lock); 256 vput(lowervp); 257 vp->v_object = NULL; 258 null_destroy_proto(vp, xp); 259 return (error); 260 } 261 262 null_hashins(mp, xp); 263 vn_set_state(vp, VSTATE_CONSTRUCTED); 264 rw_wunlock(&null_hash_lock); 265 *vpp = vp; 266 267 return (0); 268 } 269 270 /* 271 * Remove node from hash. 272 */ 273 void 274 null_hashrem(struct null_node *xp) 275 { 276 277 rw_wlock(&null_hash_lock); 278 LIST_REMOVE(xp, null_hash); 279 rw_wunlock(&null_hash_lock); 280 } 281 282 #ifdef DIAGNOSTIC 283 284 struct vnode * 285 null_checkvp(struct vnode *vp, char *fil, int lno) 286 { 287 struct null_node *a = VTONULL(vp); 288 289 #ifdef notyet 290 /* 291 * Can't do this check because vop_reclaim runs 292 * with a funny vop vector. 293 */ 294 if (vp->v_op != null_vnodeop_p) { 295 printf ("null_checkvp: on non-null-node\n"); 296 panic("null_checkvp"); 297 } 298 #endif 299 if (a->null_lowervp == NULLVP) { 300 /* Should never happen */ 301 panic("null_checkvp %p", vp); 302 } 303 VI_LOCK_FLAGS(a->null_lowervp, MTX_DUPOK); 304 if (a->null_lowervp->v_usecount < 1) 305 panic ("null with unref'ed lowervp, vp %p lvp %p", 306 vp, a->null_lowervp); 307 VI_UNLOCK(a->null_lowervp); 308 #ifdef notyet 309 printf("null %x/%d -> %x/%d [%s, %d]\n", 310 NULLTOV(a), vrefcnt(NULLTOV(a)), 311 a->null_lowervp, vrefcnt(a->null_lowervp), 312 fil, lno); 313 #endif 314 return (a->null_lowervp); 315 } 316 #endif 317