1139776Simp/*-
21541Srgrimes * Copyright (c) 1992, 1993
31541Srgrimes *	The Regents of the University of California.  All rights reserved.
41541Srgrimes *
51541Srgrimes * This code is derived from software donated to Berkeley by
61541Srgrimes * Jan-Simon Pendry.
71541Srgrimes *
81541Srgrimes * Redistribution and use in source and binary forms, with or without
91541Srgrimes * modification, are permitted provided that the following conditions
101541Srgrimes * are met:
111541Srgrimes * 1. Redistributions of source code must retain the above copyright
121541Srgrimes *    notice, this list of conditions and the following disclaimer.
131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
141541Srgrimes *    notice, this list of conditions and the following disclaimer in the
151541Srgrimes *    documentation and/or other materials provided with the distribution.
161541Srgrimes * 4. Neither the name of the University nor the names of its contributors
171541Srgrimes *    may be used to endorse or promote products derived from this software
181541Srgrimes *    without specific prior written permission.
191541Srgrimes *
201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
231541Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
301541Srgrimes * SUCH DAMAGE.
311541Srgrimes *
3222521Sdyson *	@(#)null_subr.c	8.7 (Berkeley) 5/14/95
331541Srgrimes *
3450477Speter * $FreeBSD$
351541Srgrimes */
361541Srgrimes
371541Srgrimes#include <sys/param.h>
381541Srgrimes#include <sys/systm.h>
3965467Sbp#include <sys/kernel.h>
4076166Smarkm#include <sys/lock.h>
4198177Ssemenu#include <sys/mutex.h>
4276166Smarkm#include <sys/malloc.h>
4376166Smarkm#include <sys/mount.h>
4476166Smarkm#include <sys/proc.h>
451541Srgrimes#include <sys/vnode.h>
4676166Smarkm
4777031Sru#include <fs/nullfs/null.h>
481541Srgrimes
491541Srgrimes/*
501541Srgrimes * Null layer cache:
511541Srgrimes * Each cache entry holds a reference to the lower vnode
521541Srgrimes * along with a pointer to the alias vnode.  When an
531541Srgrimes * entry is added the lower vnode is VREF'd.  When the
541541Srgrimes * alias is removed the lower vnode is vrele'd.
551541Srgrimes */
561541Srgrimes
57245408Skib#define	NULL_NHASH(vp) (&null_node_hashtbl[vfs_hash_index(vp) & null_hash_mask])
5865467Sbp
5960938Sjakestatic LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
60245408Skibstatic struct mtx null_hashmtx;
61245408Skibstatic u_long null_hash_mask;
621541Srgrimes
63151897Srwatsonstatic MALLOC_DEFINE(M_NULLFSHASH, "nullfs_hash", "NULLFS hash table");
64151897SrwatsonMALLOC_DEFINE(M_NULLFSNODE, "nullfs_node", "NULLFS vnode private part");
6565467Sbp
66116469Stjrstatic struct vnode * null_hashins(struct mount *, struct null_node *);
6712595Sbde
681541Srgrimes/*
691541Srgrimes * Initialise cache headers
701541Srgrimes */
711549Srgrimesint
7222521Sdysonnullfs_init(vfsp)
7322521Sdyson	struct vfsconf *vfsp;
741541Srgrimes{
7522521Sdyson
76245408Skib	null_node_hashtbl = hashinit(desiredvnodes, M_NULLFSHASH,
77245408Skib	    &null_hash_mask);
7898177Ssemenu	mtx_init(&null_hashmtx, "nullhs", NULL, MTX_DEF);
791549Srgrimes	return (0);
801541Srgrimes}
811541Srgrimes
8265467Sbpint
8365467Sbpnullfs_uninit(vfsp)
8465467Sbp	struct vfsconf *vfsp;
8565467Sbp{
8665467Sbp
8798177Ssemenu	mtx_destroy(&null_hashmtx);
88245408Skib	hashdestroy(null_node_hashtbl, M_NULLFSHASH, null_hash_mask);
8965467Sbp	return (0);
9065467Sbp}
9165467Sbp
921541Srgrimes/*
931541Srgrimes * Return a VREF'ed alias for lower vnode if already exists, else 0.
9466356Sbp * Lower vnode should be locked on entry and will be left locked on exit.
951541Srgrimes */
96240285Skibstruct vnode *
97116469Stjrnull_hashget(mp, lowervp)
98116469Stjr	struct mount *mp;
991541Srgrimes	struct vnode *lowervp;
1001541Srgrimes{
10122521Sdyson	struct null_node_hashhead *hd;
1021541Srgrimes	struct null_node *a;
1031541Srgrimes	struct vnode *vp;
1041541Srgrimes
105155898Sjeff	ASSERT_VOP_LOCKED(lowervp, "null_hashget");
106155898Sjeff
1071541Srgrimes	/*
1081541Srgrimes	 * Find hash base, and then search the (two-way) linked
1091541Srgrimes	 * list looking for a null_node structure which is referencing
1101541Srgrimes	 * the lower vnode.  If found, the increment the null_node
1111541Srgrimes	 * reference count (but NOT the lower vnode's VREF counter).
1121541Srgrimes	 */
11322521Sdyson	hd = NULL_NHASH(lowervp);
11498177Ssemenu	mtx_lock(&null_hashmtx);
11571999Sphk	LIST_FOREACH(a, hd, null_hash) {
116116469Stjr		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
117144904Sjeff			/*
118155898Sjeff			 * Since we have the lower node locked the nullfs
119155898Sjeff			 * node can not be in the process of recycling.  If
120155898Sjeff			 * it had been recycled before we grabed the lower
121155898Sjeff			 * lock it would not have been found on the hash.
122155898Sjeff			 */
123177725Sjeff			vp = NULLTOV(a);
124177725Sjeff			vref(vp);
125177725Sjeff			mtx_unlock(&null_hashmtx);
1261541Srgrimes			return (vp);
1271541Srgrimes		}
1281541Srgrimes	}
12998177Ssemenu	mtx_unlock(&null_hashmtx);
13098183Ssemenu	return (NULLVP);
13198183Ssemenu}
1321541Srgrimes
13398183Ssemenu/*
13498183Ssemenu * Act like null_hashget, but add passed null_node to hash if no existing
13598183Ssemenu * node found.
13698183Ssemenu */
13798183Ssemenustatic struct vnode *
138116469Stjrnull_hashins(mp, xp)
139116469Stjr	struct mount *mp;
14098183Ssemenu	struct null_node *xp;
14198183Ssemenu{
14298183Ssemenu	struct null_node_hashhead *hd;
14398183Ssemenu	struct null_node *oxp;
14498183Ssemenu	struct vnode *ovp;
14598183Ssemenu
14698183Ssemenu	hd = NULL_NHASH(xp->null_lowervp);
14798183Ssemenu	mtx_lock(&null_hashmtx);
14898183Ssemenu	LIST_FOREACH(oxp, hd, null_hash) {
149116469Stjr		if (oxp->null_lowervp == xp->null_lowervp &&
150116469Stjr		    NULLTOV(oxp)->v_mount == mp) {
151155898Sjeff			/*
152155898Sjeff			 * See null_hashget for a description of this
153155898Sjeff			 * operation.
154155898Sjeff			 */
15598183Ssemenu			ovp = NULLTOV(oxp);
156177725Sjeff			vref(ovp);
157143642Sjeff			mtx_unlock(&null_hashmtx);
15898183Ssemenu			return (ovp);
15998183Ssemenu		}
16098183Ssemenu	}
16198183Ssemenu	LIST_INSERT_HEAD(hd, xp, null_hash);
16298183Ssemenu	mtx_unlock(&null_hashmtx);
16398183Ssemenu	return (NULLVP);
1641541Srgrimes}
1651541Srgrimes
166167497Steggestatic void
167232299Skibnull_destroy_proto(struct vnode *vp, void *xp)
168167497Stegge{
169229431Skib
170232383Skib	lockmgr(&vp->v_lock, LK_EXCLUSIVE, NULL);
171232299Skib	VI_LOCK(vp);
172167497Stegge	vp->v_data = NULL;
173167497Stegge	vp->v_vnlock = &vp->v_lock;
174167497Stegge	vp->v_op = &dead_vnodeops;
175232299Skib	VI_UNLOCK(vp);
176167497Stegge	vgone(vp);
177167497Stegge	vput(vp);
178232299Skib	free(xp, M_NULLFSNODE);
179167497Stegge}
180167497Stegge
181232299Skibstatic void
182232299Skibnull_insmntque_dtr(struct vnode *vp, void *xp)
183232299Skib{
184232299Skib
185232299Skib	vput(((struct null_node *)xp)->null_lowervp);
186232299Skib	null_destroy_proto(vp, xp);
187232299Skib}
188232299Skib
1891541Srgrimes/*
19098183Ssemenu * Make a new or get existing nullfs node.
19198183Ssemenu * Vp is the alias vnode, lowervp is the lower vnode.
19298183Ssemenu *
19398183Ssemenu * The lowervp assumed to be locked and having "spare" reference. This routine
19498183Ssemenu * vrele lowervp if nullfs node was taken from hash. Otherwise it "transfers"
19598183Ssemenu * the caller's "spare" reference to created nullfs vnode.
1961541Srgrimes */
19798183Ssemenuint
19898183Ssemenunull_nodeget(mp, lowervp, vpp)
1991541Srgrimes	struct mount *mp;
2001541Srgrimes	struct vnode *lowervp;
2011541Srgrimes	struct vnode **vpp;
2021541Srgrimes{
2031541Srgrimes	struct null_node *xp;
20498183Ssemenu	struct vnode *vp;
2051541Srgrimes	int error;
2061541Srgrimes
207240285Skib	ASSERT_VOP_LOCKED(lowervp, "lowervp");
208240285Skib	KASSERT(lowervp->v_usecount >= 1, ("Unreferenced vnode %p", lowervp));
209229428Skib
210240285Skib	/* Lookup the hash firstly. */
211116469Stjr	*vpp = null_hashget(mp, lowervp);
21298183Ssemenu	if (*vpp != NULL) {
21398183Ssemenu		vrele(lowervp);
21498183Ssemenu		return (0);
21598183Ssemenu	}
21698183Ssemenu
21716312Sdg	/*
218240285Skib	 * The insmntque1() call below requires the exclusive lock on
219240285Skib	 * the nullfs vnode.  Upgrade the lock now if hash failed to
220240285Skib	 * provide ready to use vnode.
221240285Skib	 */
222240285Skib	if (VOP_ISLOCKED(lowervp) != LK_EXCLUSIVE) {
223245033Skib		KASSERT((MOUNTTONULLMOUNT(mp)->nullm_flags & NULLM_CACHE) != 0,
224245004Skib		    ("lowervp %p is not excl locked and cache is disabled",
225245004Skib		    lowervp));
226240285Skib		vn_lock(lowervp, LK_UPGRADE | LK_RETRY);
227240285Skib		if ((lowervp->v_iflag & VI_DOOMED) != 0) {
228240285Skib			vput(lowervp);
229240285Skib			return (ENOENT);
230240285Skib		}
231240285Skib	}
232240285Skib
233240285Skib	/*
23498183Ssemenu	 * We do not serialize vnode creation, instead we will check for
23598183Ssemenu	 * duplicates later, when adding new vnode to hash.
23698183Ssemenu	 * Note that duplicate can only appear in hash if the lowervp is
23798183Ssemenu	 * locked LK_SHARED.
23816312Sdg	 */
239240285Skib	xp = malloc(sizeof(struct null_node), M_NULLFSNODE, M_WAITOK);
24016312Sdg
241138290Sphk	error = getnewvnode("null", mp, &null_vnodeops, &vp);
24216312Sdg	if (error) {
243229431Skib		vput(lowervp);
244184205Sdes		free(xp, M_NULLFSNODE);
2451541Srgrimes		return (error);
24616312Sdg	}
2471541Srgrimes
24898176Ssemenu	xp->null_vnode = vp;
24998176Ssemenu	xp->null_lowervp = lowervp;
250250505Skib	xp->null_flags = 0;
2511541Srgrimes	vp->v_type = lowervp->v_type;
2521541Srgrimes	vp->v_data = xp;
25366356Sbp	vp->v_vnlock = lowervp->v_vnlock;
254167497Stegge	error = insmntque1(vp, mp, null_insmntque_dtr, xp);
255167497Stegge	if (error != 0)
256167497Stegge		return (error);
25798183Ssemenu	/*
25898183Ssemenu	 * Atomically insert our new node into the hash or vget existing
25998183Ssemenu	 * if someone else has beaten us to it.
26098183Ssemenu	 */
261116469Stjr	*vpp = null_hashins(mp, xp);
26298183Ssemenu	if (*vpp != NULL) {
26398183Ssemenu		vrele(lowervp);
264232299Skib		null_destroy_proto(vp, xp);
26598183Ssemenu		return (0);
2661541Srgrimes	}
26798183Ssemenu	*vpp = vp;
2681541Srgrimes
2691541Srgrimes	return (0);
2701541Srgrimes}
27128832Skato
27298183Ssemenu/*
27398183Ssemenu * Remove node from hash.
27498183Ssemenu */
27598176Ssemenuvoid
27698176Ssemenunull_hashrem(xp)
27798176Ssemenu	struct null_node *xp;
27898176Ssemenu{
27998176Ssemenu
28098177Ssemenu	mtx_lock(&null_hashmtx);
28198176Ssemenu	LIST_REMOVE(xp, null_hash);
28298177Ssemenu	mtx_unlock(&null_hashmtx);
28398176Ssemenu}
28498176Ssemenu
28550890Sbde#ifdef DIAGNOSTIC
28628844Skato
2871541Srgrimesstruct vnode *
2881541Srgrimesnull_checkvp(vp, fil, lno)
2891541Srgrimes	struct vnode *vp;
2901541Srgrimes	char *fil;
2911541Srgrimes	int lno;
2921541Srgrimes{
2931541Srgrimes	struct null_node *a = VTONULL(vp);
294193173Skib
2951541Srgrimes#ifdef notyet
2961541Srgrimes	/*
2971541Srgrimes	 * Can't do this check because vop_reclaim runs
2981541Srgrimes	 * with a funny vop vector.
2991541Srgrimes	 */
3001541Srgrimes	if (vp->v_op != null_vnodeop_p) {
3011541Srgrimes		printf ("null_checkvp: on non-null-node\n");
3021541Srgrimes		panic("null_checkvp");
303193173Skib	}
3041541Srgrimes#endif
30524987Skato	if (a->null_lowervp == NULLVP) {
3061541Srgrimes		/* Should never happen */
307227695Skib		panic("null_checkvp %p", vp);
3081541Srgrimes	}
309193173Skib	VI_LOCK_FLAGS(a->null_lowervp, MTX_DUPOK);
310227695Skib	if (a->null_lowervp->v_usecount < 1)
311227695Skib		panic ("null with unref'ed lowervp, vp %p lvp %p",
312227695Skib		    vp, a->null_lowervp);
313193173Skib	VI_UNLOCK(a->null_lowervp);
3141541Srgrimes#ifdef notyet
3151541Srgrimes	printf("null %x/%d -> %x/%d [%s, %d]\n",
316103936Sjeff	        NULLTOV(a), vrefcnt(NULLTOV(a)),
317103936Sjeff		a->null_lowervp, vrefcnt(a->null_lowervp),
3181541Srgrimes		fil, lno);
3191541Srgrimes#endif
320193173Skib	return (a->null_lowervp);
3211541Srgrimes}
3221541Srgrimes#endif
323