1d167cf6fSWarner Losh /*- 251369649SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause 351369649SPedro F. Giffuni * 4df8bae1dSRodney W. Grimes * Copyright (c) 1992, 1993 5df8bae1dSRodney W. Grimes * The Regents of the University of California. All rights reserved. 6df8bae1dSRodney W. Grimes * 7df8bae1dSRodney W. Grimes * This code is derived from software donated to Berkeley by 8df8bae1dSRodney W. Grimes * Jan-Simon Pendry. 9df8bae1dSRodney W. Grimes * 10df8bae1dSRodney W. Grimes * Redistribution and use in source and binary forms, with or without 11df8bae1dSRodney W. Grimes * modification, are permitted provided that the following conditions 12df8bae1dSRodney W. Grimes * are met: 13df8bae1dSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright 14df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer. 15df8bae1dSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright 16df8bae1dSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the 17df8bae1dSRodney W. Grimes * documentation and/or other materials provided with the distribution. 18fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 19df8bae1dSRodney W. Grimes * may be used to endorse or promote products derived from this software 20df8bae1dSRodney W. Grimes * without specific prior written permission. 21df8bae1dSRodney W. Grimes * 22df8bae1dSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23df8bae1dSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24df8bae1dSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25df8bae1dSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26df8bae1dSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27df8bae1dSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28df8bae1dSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29df8bae1dSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30df8bae1dSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31df8bae1dSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32df8bae1dSRodney W. Grimes * SUCH DAMAGE. 33df8bae1dSRodney W. Grimes * 34996c772fSJohn Dyson * @(#)null_subr.c 8.7 (Berkeley) 5/14/95 35df8bae1dSRodney W. Grimes * 36c3aac50fSPeter Wemm * $FreeBSD$ 37df8bae1dSRodney W. Grimes */ 38df8bae1dSRodney W. Grimes 39df8bae1dSRodney W. Grimes #include <sys/param.h> 40df8bae1dSRodney W. Grimes #include <sys/systm.h> 418da80660SBoris Popov #include <sys/kernel.h> 42fb919e4dSMark Murray #include <sys/lock.h> 43cd29b292SMateusz Guzik #include <sys/rwlock.h> 44df8bae1dSRodney W. Grimes #include <sys/malloc.h> 45fb919e4dSMark Murray #include <sys/mount.h> 46fb919e4dSMark Murray #include <sys/proc.h> 47fb919e4dSMark Murray #include <sys/vnode.h> 48fb919e4dSMark Murray 4999d300a1SRuslan Ermilov #include <fs/nullfs/null.h> 50df8bae1dSRodney W. Grimes 51df8bae1dSRodney W. Grimes /* 52df8bae1dSRodney W. Grimes * Null layer cache: 53df8bae1dSRodney W. Grimes * Each cache entry holds a reference to the lower vnode 54df8bae1dSRodney W. Grimes * along with a pointer to the alias vnode. When an 55df8bae1dSRodney W. Grimes * entry is added the lower vnode is VREF'd. When the 56df8bae1dSRodney W. Grimes * alias is removed the lower vnode is vrele'd. 57df8bae1dSRodney W. Grimes */ 58df8bae1dSRodney W. Grimes 59603f963eSKonstantin Belousov #define NULL_NHASH(vp) (&null_node_hashtbl[vfs_hash_index(vp) & null_hash_mask]) 608da80660SBoris Popov 61e3975643SJake Burkholder static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl; 62cd29b292SMateusz Guzik static struct rwlock null_hash_lock; 63603f963eSKonstantin Belousov static u_long null_hash_mask; 648da80660SBoris Popov 655bb84bc8SRobert Watson static MALLOC_DEFINE(M_NULLFSHASH, "nullfs_hash", "NULLFS hash table"); 665bb84bc8SRobert Watson MALLOC_DEFINE(M_NULLFSNODE, "nullfs_node", "NULLFS vnode private part"); 67df8bae1dSRodney W. Grimes 6854939875STim J. Robbins static struct vnode * null_hashins(struct mount *, struct null_node *); 699b5e8b3aSBruce Evans 70df8bae1dSRodney W. Grimes /* 71df8bae1dSRodney W. Grimes * Initialise cache headers 72df8bae1dSRodney W. Grimes */ 7326f9a767SRodney W. Grimes int 74996c772fSJohn Dyson nullfs_init(vfsp) 75996c772fSJohn Dyson struct vfsconf *vfsp; 76df8bae1dSRodney W. Grimes { 77996c772fSJohn Dyson 78603f963eSKonstantin Belousov null_node_hashtbl = hashinit(desiredvnodes, M_NULLFSHASH, 79603f963eSKonstantin Belousov &null_hash_mask); 80cd29b292SMateusz Guzik rw_init(&null_hash_lock, "nullhs"); 818da80660SBoris Popov return (0); 828da80660SBoris Popov } 838da80660SBoris Popov 848da80660SBoris Popov int 858da80660SBoris Popov nullfs_uninit(vfsp) 868da80660SBoris Popov struct vfsconf *vfsp; 878da80660SBoris Popov { 888da80660SBoris Popov 89cd29b292SMateusz Guzik rw_destroy(&null_hash_lock); 90603f963eSKonstantin Belousov hashdestroy(null_node_hashtbl, M_NULLFSHASH, null_hash_mask); 9126f9a767SRodney W. Grimes return (0); 92df8bae1dSRodney W. Grimes } 93df8bae1dSRodney W. Grimes 94df8bae1dSRodney W. Grimes /* 95df8bae1dSRodney W. Grimes * Return a VREF'ed alias for lower vnode if already exists, else 0. 964451405fSBoris Popov * Lower vnode should be locked on entry and will be left locked on exit. 97df8bae1dSRodney W. Grimes */ 98d9e9650aSKonstantin Belousov struct vnode * 9954939875STim J. Robbins null_hashget(mp, lowervp) 10054939875STim J. Robbins struct mount *mp; 101df8bae1dSRodney W. Grimes struct vnode *lowervp; 102df8bae1dSRodney W. Grimes { 103996c772fSJohn Dyson struct null_node_hashhead *hd; 104df8bae1dSRodney W. Grimes struct null_node *a; 105df8bae1dSRodney W. Grimes struct vnode *vp; 1069c12e631SJeff Roberson 1079c12e631SJeff Roberson ASSERT_VOP_LOCKED(lowervp, "null_hashget"); 108df8bae1dSRodney W. Grimes 109df8bae1dSRodney W. Grimes /* 110df8bae1dSRodney W. Grimes * Find hash base, and then search the (two-way) linked 111df8bae1dSRodney W. Grimes * list looking for a null_node structure which is referencing 112df8bae1dSRodney W. Grimes * the lower vnode. If found, the increment the null_node 113df8bae1dSRodney W. Grimes * reference count (but NOT the lower vnode's VREF counter). 114df8bae1dSRodney W. Grimes */ 115996c772fSJohn Dyson hd = NULL_NHASH(lowervp); 1161e0006e4SMateusz Guzik if (LIST_EMPTY(hd)) 1171e0006e4SMateusz Guzik return (NULLVP); 118cd29b292SMateusz Guzik rw_rlock(&null_hash_lock); 119fc2ffbe6SPoul-Henning Kamp LIST_FOREACH(a, hd, null_hash) { 12054939875STim J. Robbins if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) { 1219c12e631SJeff Roberson /* 1229c12e631SJeff Roberson * Since we have the lower node locked the nullfs 1239c12e631SJeff Roberson * node can not be in the process of recycling. If 1249c12e631SJeff Roberson * it had been recycled before we grabed the lower 1259c12e631SJeff Roberson * lock it would not have been found on the hash. 1269c12e631SJeff Roberson */ 1274c65d593SJeff Roberson vp = NULLTOV(a); 1284c65d593SJeff Roberson vref(vp); 129cd29b292SMateusz Guzik rw_runlock(&null_hash_lock); 130df8bae1dSRodney W. Grimes return (vp); 131df8bae1dSRodney W. Grimes } 132df8bae1dSRodney W. Grimes } 133cd29b292SMateusz Guzik rw_runlock(&null_hash_lock); 1341cfdefbbSSemen Ustimenko return (NULLVP); 135df8bae1dSRodney W. Grimes } 136df8bae1dSRodney W. Grimes 1371cfdefbbSSemen Ustimenko /* 1381cfdefbbSSemen Ustimenko * Act like null_hashget, but add passed null_node to hash if no existing 1391cfdefbbSSemen Ustimenko * node found. 1401cfdefbbSSemen Ustimenko */ 1411cfdefbbSSemen Ustimenko static struct vnode * 14254939875STim J. Robbins null_hashins(mp, xp) 14354939875STim J. Robbins struct mount *mp; 1441cfdefbbSSemen Ustimenko struct null_node *xp; 1451cfdefbbSSemen Ustimenko { 1461cfdefbbSSemen Ustimenko struct null_node_hashhead *hd; 1471cfdefbbSSemen Ustimenko struct null_node *oxp; 1481cfdefbbSSemen Ustimenko struct vnode *ovp; 1491cfdefbbSSemen Ustimenko 1501cfdefbbSSemen Ustimenko hd = NULL_NHASH(xp->null_lowervp); 151cd29b292SMateusz Guzik rw_wlock(&null_hash_lock); 1521cfdefbbSSemen Ustimenko LIST_FOREACH(oxp, hd, null_hash) { 15354939875STim J. Robbins if (oxp->null_lowervp == xp->null_lowervp && 15454939875STim J. Robbins NULLTOV(oxp)->v_mount == mp) { 1559c12e631SJeff Roberson /* 1569c12e631SJeff Roberson * See null_hashget for a description of this 1579c12e631SJeff Roberson * operation. 1589c12e631SJeff Roberson */ 1591cfdefbbSSemen Ustimenko ovp = NULLTOV(oxp); 1604c65d593SJeff Roberson vref(ovp); 161cd29b292SMateusz Guzik rw_wunlock(&null_hash_lock); 1621cfdefbbSSemen Ustimenko return (ovp); 1631cfdefbbSSemen Ustimenko } 1641cfdefbbSSemen Ustimenko } 1651cfdefbbSSemen Ustimenko LIST_INSERT_HEAD(hd, xp, null_hash); 166cd29b292SMateusz Guzik rw_wunlock(&null_hash_lock); 1671cfdefbbSSemen Ustimenko return (NULLVP); 1681cfdefbbSSemen Ustimenko } 169df8bae1dSRodney W. Grimes 17061b9d89fSTor Egge static void 17167e3d54fSKonstantin Belousov null_destroy_proto(struct vnode *vp, void *xp) 17267e3d54fSKonstantin Belousov { 17367e3d54fSKonstantin Belousov 17466f02f4bSKonstantin Belousov lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL); 17567e3d54fSKonstantin Belousov VI_LOCK(vp); 17667e3d54fSKonstantin Belousov vp->v_data = NULL; 17767e3d54fSKonstantin Belousov vp->v_vnlock = &vp->v_lock; 17867e3d54fSKonstantin Belousov vp->v_op = &dead_vnodeops; 17967e3d54fSKonstantin Belousov VI_UNLOCK(vp); 18067e3d54fSKonstantin Belousov vgone(vp); 18167e3d54fSKonstantin Belousov vput(vp); 18267e3d54fSKonstantin Belousov free(xp, M_NULLFSNODE); 18367e3d54fSKonstantin Belousov } 18467e3d54fSKonstantin Belousov 185df8bae1dSRodney W. Grimes /* 1861cfdefbbSSemen Ustimenko * Make a new or get existing nullfs node. 1871cfdefbbSSemen Ustimenko * Vp is the alias vnode, lowervp is the lower vnode. 1881cfdefbbSSemen Ustimenko * 1891cfdefbbSSemen Ustimenko * The lowervp assumed to be locked and having "spare" reference. This routine 1901cfdefbbSSemen Ustimenko * vrele lowervp if nullfs node was taken from hash. Otherwise it "transfers" 1911cfdefbbSSemen Ustimenko * the caller's "spare" reference to created nullfs vnode. 192df8bae1dSRodney W. Grimes */ 1931cfdefbbSSemen Ustimenko int 1941cfdefbbSSemen Ustimenko null_nodeget(mp, lowervp, vpp) 195df8bae1dSRodney W. Grimes struct mount *mp; 196df8bae1dSRodney W. Grimes struct vnode *lowervp; 197df8bae1dSRodney W. Grimes struct vnode **vpp; 198df8bae1dSRodney W. Grimes { 199df8bae1dSRodney W. Grimes struct null_node *xp; 2001cfdefbbSSemen Ustimenko struct vnode *vp; 201df8bae1dSRodney W. Grimes int error; 202df8bae1dSRodney W. Grimes 203d9e9650aSKonstantin Belousov ASSERT_VOP_LOCKED(lowervp, "lowervp"); 204f1fa1ba3SMateusz Guzik VNPASS(lowervp->v_usecount > 0, lowervp); 20548a1e3f6SKonstantin Belousov 206d9e9650aSKonstantin Belousov /* Lookup the hash firstly. */ 20754939875STim J. Robbins *vpp = null_hashget(mp, lowervp); 2081cfdefbbSSemen Ustimenko if (*vpp != NULL) { 2091cfdefbbSSemen Ustimenko vrele(lowervp); 2101cfdefbbSSemen Ustimenko return (0); 2111cfdefbbSSemen Ustimenko } 2121cfdefbbSSemen Ustimenko 2131cfdefbbSSemen Ustimenko /* 214*4e91a0b9SMateusz Guzik * The insmntque() call below requires the exclusive lock on 215d9e9650aSKonstantin Belousov * the nullfs vnode. Upgrade the lock now if hash failed to 216d9e9650aSKonstantin Belousov * provide ready to use vnode. 217d9e9650aSKonstantin Belousov */ 218d9e9650aSKonstantin Belousov if (VOP_ISLOCKED(lowervp) != LK_EXCLUSIVE) { 219d9e9650aSKonstantin Belousov vn_lock(lowervp, LK_UPGRADE | LK_RETRY); 220abd80ddbSMateusz Guzik if (VN_IS_DOOMED(lowervp)) { 221d9e9650aSKonstantin Belousov vput(lowervp); 222d9e9650aSKonstantin Belousov return (ENOENT); 223d9e9650aSKonstantin Belousov } 224d9e9650aSKonstantin Belousov } 225d9e9650aSKonstantin Belousov 226d9e9650aSKonstantin Belousov /* 2271cfdefbbSSemen Ustimenko * We do not serialize vnode creation, instead we will check for 2281cfdefbbSSemen Ustimenko * duplicates later, when adding new vnode to hash. 2291cfdefbbSSemen Ustimenko * Note that duplicate can only appear in hash if the lowervp is 2301cfdefbbSSemen Ustimenko * locked LK_SHARED. 2312f9bae59SDavid Greenman */ 232d9e9650aSKonstantin Belousov xp = malloc(sizeof(struct null_node), M_NULLFSNODE, M_WAITOK); 2332f9bae59SDavid Greenman 234e583d999SEdward Tomasz Napierala error = getnewvnode("nullfs", mp, &null_vnodeops, &vp); 2352f9bae59SDavid Greenman if (error) { 236dd0f9532SKonstantin Belousov vput(lowervp); 2371ede983cSDag-Erling Smørgrav free(xp, M_NULLFSNODE); 238df8bae1dSRodney W. Grimes return (error); 2392f9bae59SDavid Greenman } 240df8bae1dSRodney W. Grimes 241df8bae1dSRodney W. Grimes xp->null_vnode = vp; 242df8bae1dSRodney W. Grimes xp->null_lowervp = lowervp; 2430fc6daa7SKonstantin Belousov xp->null_flags = 0; 24408720e34SSemen Ustimenko vp->v_type = lowervp->v_type; 24508720e34SSemen Ustimenko vp->v_data = xp; 2464451405fSBoris Popov vp->v_vnlock = lowervp->v_vnlock; 247*4e91a0b9SMateusz Guzik error = insmntque(vp, mp); 248*4e91a0b9SMateusz Guzik if (error != 0) { 249*4e91a0b9SMateusz Guzik vput(lowervp); 250*4e91a0b9SMateusz Guzik null_destroy_proto(vp, xp); 25161b9d89fSTor Egge return (error); 252*4e91a0b9SMateusz Guzik } 253dc1d2cc6SKonstantin Belousov if (lowervp == MOUNTTONULLMOUNT(mp)->nullm_lowerrootvp) 254dc1d2cc6SKonstantin Belousov vp->v_vflag |= VV_ROOT; 255dc1d2cc6SKonstantin Belousov 256df8bae1dSRodney W. Grimes /* 257685cb01aSKonstantin Belousov * We might miss the case where lower vnode sets VIRF_PGREAD 258685cb01aSKonstantin Belousov * some time after construction, which is typical case. 259685cb01aSKonstantin Belousov * null_open rechecks. 260685cb01aSKonstantin Belousov */ 2613e506a67SMateusz Guzik if ((vn_irflag_read(lowervp) & VIRF_PGREAD) != 0) { 262685cb01aSKonstantin Belousov MPASS(lowervp->v_object != NULL); 2633e506a67SMateusz Guzik if ((vn_irflag_read(vp) & VIRF_PGREAD) == 0) { 264685cb01aSKonstantin Belousov if (vp->v_object == NULL) 265685cb01aSKonstantin Belousov vp->v_object = lowervp->v_object; 266685cb01aSKonstantin Belousov else 267685cb01aSKonstantin Belousov MPASS(vp->v_object == lowervp->v_object); 2683e506a67SMateusz Guzik vn_irflag_set_cond(vp, VIRF_PGREAD); 269685cb01aSKonstantin Belousov } else { 270685cb01aSKonstantin Belousov MPASS(vp->v_object != NULL); 271685cb01aSKonstantin Belousov } 272685cb01aSKonstantin Belousov } 273685cb01aSKonstantin Belousov 274685cb01aSKonstantin Belousov /* 2751cfdefbbSSemen Ustimenko * Atomically insert our new node into the hash or vget existing 2761cfdefbbSSemen Ustimenko * if someone else has beaten us to it. 277df8bae1dSRodney W. Grimes */ 27854939875STim J. Robbins *vpp = null_hashins(mp, xp); 2791cfdefbbSSemen Ustimenko if (*vpp != NULL) { 2804451405fSBoris Popov vrele(lowervp); 281685cb01aSKonstantin Belousov vp->v_object = NULL; /* in case VIRF_PGREAD set it */ 28267e3d54fSKonstantin Belousov null_destroy_proto(vp, xp); 283df8bae1dSRodney W. Grimes return (0); 284df8bae1dSRodney W. Grimes } 2851cfdefbbSSemen Ustimenko *vpp = vp; 2861cfdefbbSSemen Ustimenko 2871cfdefbbSSemen Ustimenko return (0); 2881cfdefbbSSemen Ustimenko } 2891cfdefbbSSemen Ustimenko 2901cfdefbbSSemen Ustimenko /* 2911cfdefbbSSemen Ustimenko * Remove node from hash. 2921cfdefbbSSemen Ustimenko */ 29308720e34SSemen Ustimenko void 29408720e34SSemen Ustimenko null_hashrem(xp) 29508720e34SSemen Ustimenko struct null_node *xp; 29608720e34SSemen Ustimenko { 29708720e34SSemen Ustimenko 298cd29b292SMateusz Guzik rw_wlock(&null_hash_lock); 29908720e34SSemen Ustimenko LIST_REMOVE(xp, null_hash); 300cd29b292SMateusz Guzik rw_wunlock(&null_hash_lock); 30108720e34SSemen Ustimenko } 30208720e34SSemen Ustimenko 303a0f40f54SBruce Evans #ifdef DIAGNOSTIC 3041bf978ceSKATO Takenori 305df8bae1dSRodney W. Grimes struct vnode * 306df8bae1dSRodney W. Grimes null_checkvp(vp, fil, lno) 307df8bae1dSRodney W. Grimes struct vnode *vp; 308df8bae1dSRodney W. Grimes char *fil; 309df8bae1dSRodney W. Grimes int lno; 310df8bae1dSRodney W. Grimes { 311df8bae1dSRodney W. Grimes struct null_node *a = VTONULL(vp); 312b9131889SKonstantin Belousov 313df8bae1dSRodney W. Grimes #ifdef notyet 314df8bae1dSRodney W. Grimes /* 315df8bae1dSRodney W. Grimes * Can't do this check because vop_reclaim runs 316df8bae1dSRodney W. Grimes * with a funny vop vector. 317df8bae1dSRodney W. Grimes */ 318df8bae1dSRodney W. Grimes if (vp->v_op != null_vnodeop_p) { 319df8bae1dSRodney W. Grimes printf ("null_checkvp: on non-null-node\n"); 320df8bae1dSRodney W. Grimes panic("null_checkvp"); 321b9131889SKonstantin Belousov } 322df8bae1dSRodney W. Grimes #endif 323c5e17d9eSKATO Takenori if (a->null_lowervp == NULLVP) { 324df8bae1dSRodney W. Grimes /* Should never happen */ 3254d2310ddSKonstantin Belousov panic("null_checkvp %p", vp); 326df8bae1dSRodney W. Grimes } 327b9131889SKonstantin Belousov VI_LOCK_FLAGS(a->null_lowervp, MTX_DUPOK); 3284d2310ddSKonstantin Belousov if (a->null_lowervp->v_usecount < 1) 3294d2310ddSKonstantin Belousov panic ("null with unref'ed lowervp, vp %p lvp %p", 3304d2310ddSKonstantin Belousov vp, a->null_lowervp); 331b9131889SKonstantin Belousov VI_UNLOCK(a->null_lowervp); 332df8bae1dSRodney W. Grimes #ifdef notyet 333df8bae1dSRodney W. Grimes printf("null %x/%d -> %x/%d [%s, %d]\n", 3344d93c0beSJeff Roberson NULLTOV(a), vrefcnt(NULLTOV(a)), 3354d93c0beSJeff Roberson a->null_lowervp, vrefcnt(a->null_lowervp), 336df8bae1dSRodney W. Grimes fil, lno); 337df8bae1dSRodney W. Grimes #endif 338b9131889SKonstantin Belousov return (a->null_lowervp); 339df8bae1dSRodney W. Grimes } 340df8bae1dSRodney W. Grimes #endif 341