xref: /freebsd/sys/fs/nullfs/null_subr.c (revision 8da806606149ca5b67acb2e6e9aa13cec2611cb5)
1df8bae1dSRodney W. Grimes /*
2df8bae1dSRodney W. Grimes  * Copyright (c) 1992, 1993
3df8bae1dSRodney W. Grimes  *	The Regents of the University of California.  All rights reserved.
4df8bae1dSRodney W. Grimes  *
5df8bae1dSRodney W. Grimes  * This code is derived from software donated to Berkeley by
6df8bae1dSRodney W. Grimes  * Jan-Simon Pendry.
7df8bae1dSRodney W. Grimes  *
8df8bae1dSRodney W. Grimes  * Redistribution and use in source and binary forms, with or without
9df8bae1dSRodney W. Grimes  * modification, are permitted provided that the following conditions
10df8bae1dSRodney W. Grimes  * are met:
11df8bae1dSRodney W. Grimes  * 1. Redistributions of source code must retain the above copyright
12df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer.
13df8bae1dSRodney W. Grimes  * 2. Redistributions in binary form must reproduce the above copyright
14df8bae1dSRodney W. Grimes  *    notice, this list of conditions and the following disclaimer in the
15df8bae1dSRodney W. Grimes  *    documentation and/or other materials provided with the distribution.
16df8bae1dSRodney W. Grimes  * 3. All advertising materials mentioning features or use of this software
17df8bae1dSRodney W. Grimes  *    must display the following acknowledgement:
18df8bae1dSRodney W. Grimes  *	This product includes software developed by the University of
19df8bae1dSRodney W. Grimes  *	California, Berkeley and its contributors.
20df8bae1dSRodney W. Grimes  * 4. Neither the name of the University nor the names of its contributors
21df8bae1dSRodney W. Grimes  *    may be used to endorse or promote products derived from this software
22df8bae1dSRodney W. Grimes  *    without specific prior written permission.
23df8bae1dSRodney W. Grimes  *
24df8bae1dSRodney W. Grimes  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25df8bae1dSRodney W. Grimes  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26df8bae1dSRodney W. Grimes  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27df8bae1dSRodney W. Grimes  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28df8bae1dSRodney W. Grimes  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29df8bae1dSRodney W. Grimes  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30df8bae1dSRodney W. Grimes  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31df8bae1dSRodney W. Grimes  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32df8bae1dSRodney W. Grimes  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33df8bae1dSRodney W. Grimes  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34df8bae1dSRodney W. Grimes  * SUCH DAMAGE.
35df8bae1dSRodney W. Grimes  *
36996c772fSJohn Dyson  *	@(#)null_subr.c	8.7 (Berkeley) 5/14/95
37df8bae1dSRodney W. Grimes  *
38c3aac50fSPeter Wemm  * $FreeBSD$
39df8bae1dSRodney W. Grimes  */
40df8bae1dSRodney W. Grimes 
41df8bae1dSRodney W. Grimes #include <sys/param.h>
42df8bae1dSRodney W. Grimes #include <sys/systm.h>
438da80660SBoris Popov #include <sys/kernel.h>
44996c772fSJohn Dyson #include <sys/proc.h>
45df8bae1dSRodney W. Grimes #include <sys/vnode.h>
46df8bae1dSRodney W. Grimes #include <sys/mount.h>
47df8bae1dSRodney W. Grimes #include <sys/malloc.h>
48df8bae1dSRodney W. Grimes #include <miscfs/nullfs/null.h>
49df8bae1dSRodney W. Grimes 
50df8bae1dSRodney W. Grimes #define LOG2_SIZEVNODE 7		/* log2(sizeof struct vnode) */
51df8bae1dSRodney W. Grimes #define	NNULLNODECACHE 16
52df8bae1dSRodney W. Grimes 
53df8bae1dSRodney W. Grimes /*
54df8bae1dSRodney W. Grimes  * Null layer cache:
55df8bae1dSRodney W. Grimes  * Each cache entry holds a reference to the lower vnode
56df8bae1dSRodney W. Grimes  * along with a pointer to the alias vnode.  When an
57df8bae1dSRodney W. Grimes  * entry is added the lower vnode is VREF'd.  When the
58df8bae1dSRodney W. Grimes  * alias is removed the lower vnode is vrele'd.
59df8bae1dSRodney W. Grimes  */
60df8bae1dSRodney W. Grimes 
61996c772fSJohn Dyson #define	NULL_NHASH(vp) \
62a23d65bfSBruce Evans 	(&null_node_hashtbl[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & null_node_hash])
638da80660SBoris Popov 
64e3975643SJake Burkholder static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
65303b270bSEivind Eklund static u_long null_node_hash;
668da80660SBoris Popov struct lock null_hashlock;
678da80660SBoris Popov 
688da80660SBoris Popov static MALLOC_DEFINE(M_NULLFSHASH, "NULLFS hash", "NULLFS hash table");
698da80660SBoris Popov MALLOC_DEFINE(M_NULLFSNODE, "NULLFS node", "NULLFS vnode private part");
70df8bae1dSRodney W. Grimes 
717da1e3f0SBoris Popov static int	null_node_alloc(struct mount *mp, struct vnode *lowervp,
727da1e3f0SBoris Popov 				     struct vnode **vpp);
739b5e8b3aSBruce Evans static struct vnode *
747da1e3f0SBoris Popov 		null_node_find(struct mount *mp, struct vnode *lowervp);
759b5e8b3aSBruce Evans 
76df8bae1dSRodney W. Grimes /*
77df8bae1dSRodney W. Grimes  * Initialise cache headers
78df8bae1dSRodney W. Grimes  */
7926f9a767SRodney W. Grimes int
80996c772fSJohn Dyson nullfs_init(vfsp)
81996c772fSJohn Dyson 	struct vfsconf *vfsp;
82df8bae1dSRodney W. Grimes {
83996c772fSJohn Dyson 
848da80660SBoris Popov 	NULLFSDEBUG("nullfs_init\n");		/* printed during system boot */
858da80660SBoris Popov 	null_node_hashtbl = hashinit(NNULLNODECACHE, M_NULLFSHASH, &null_node_hash);
868da80660SBoris Popov 	lockinit(&null_hashlock, PVFS, "nullhs", 0, 0);
878da80660SBoris Popov 	return (0);
888da80660SBoris Popov }
898da80660SBoris Popov 
908da80660SBoris Popov int
918da80660SBoris Popov nullfs_uninit(vfsp)
928da80660SBoris Popov 	struct vfsconf *vfsp;
938da80660SBoris Popov {
948da80660SBoris Popov 
958da80660SBoris Popov         if (null_node_hashtbl)
968da80660SBoris Popov 		free(null_node_hashtbl, M_NULLFSHASH);
9726f9a767SRodney W. Grimes 	return (0);
98df8bae1dSRodney W. Grimes }
99df8bae1dSRodney W. Grimes 
100df8bae1dSRodney W. Grimes /*
101df8bae1dSRodney W. Grimes  * Return a VREF'ed alias for lower vnode if already exists, else 0.
102df8bae1dSRodney W. Grimes  */
103df8bae1dSRodney W. Grimes static struct vnode *
104df8bae1dSRodney W. Grimes null_node_find(mp, lowervp)
105df8bae1dSRodney W. Grimes 	struct mount *mp;
106df8bae1dSRodney W. Grimes 	struct vnode *lowervp;
107df8bae1dSRodney W. Grimes {
108996c772fSJohn Dyson 	struct proc *p = curproc;	/* XXX */
109996c772fSJohn Dyson 	struct null_node_hashhead *hd;
110df8bae1dSRodney W. Grimes 	struct null_node *a;
111df8bae1dSRodney W. Grimes 	struct vnode *vp;
112df8bae1dSRodney W. Grimes 
113df8bae1dSRodney W. Grimes 	/*
114df8bae1dSRodney W. Grimes 	 * Find hash base, and then search the (two-way) linked
115df8bae1dSRodney W. Grimes 	 * list looking for a null_node structure which is referencing
116df8bae1dSRodney W. Grimes 	 * the lower vnode.  If found, the increment the null_node
117df8bae1dSRodney W. Grimes 	 * reference count (but NOT the lower vnode's VREF counter).
118df8bae1dSRodney W. Grimes 	 */
119996c772fSJohn Dyson 	hd = NULL_NHASH(lowervp);
120df8bae1dSRodney W. Grimes loop:
1218da80660SBoris Popov 	lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p);
122996c772fSJohn Dyson 	for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) {
123df8bae1dSRodney W. Grimes 		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
124df8bae1dSRodney W. Grimes 			vp = NULLTOV(a);
1258da80660SBoris Popov 			lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
126df8bae1dSRodney W. Grimes 			/*
127df8bae1dSRodney W. Grimes 			 * We need vget for the VXLOCK
128df8bae1dSRodney W. Grimes 			 * stuff, but we don't want to lock
129df8bae1dSRodney W. Grimes 			 * the lower node.
130df8bae1dSRodney W. Grimes 			 */
131996c772fSJohn Dyson 			if (vget(vp, 0, p)) {
132df8bae1dSRodney W. Grimes 				printf ("null_node_find: vget failed.\n");
133df8bae1dSRodney W. Grimes 				goto loop;
134df8bae1dSRodney W. Grimes 			};
135df8bae1dSRodney W. Grimes 			return (vp);
136df8bae1dSRodney W. Grimes 		}
137df8bae1dSRodney W. Grimes 	}
1388da80660SBoris Popov 	lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
139df8bae1dSRodney W. Grimes 
140c5e17d9eSKATO Takenori 	return NULLVP;
141df8bae1dSRodney W. Grimes }
142df8bae1dSRodney W. Grimes 
143df8bae1dSRodney W. Grimes 
144df8bae1dSRodney W. Grimes /*
145df8bae1dSRodney W. Grimes  * Make a new null_node node.
146df8bae1dSRodney W. Grimes  * Vp is the alias vnode, lofsvp is the lower vnode.
147df8bae1dSRodney W. Grimes  * Maintain a reference to (lowervp).
148df8bae1dSRodney W. Grimes  */
149df8bae1dSRodney W. Grimes static int
150df8bae1dSRodney W. Grimes null_node_alloc(mp, lowervp, vpp)
151df8bae1dSRodney W. Grimes 	struct mount *mp;
152df8bae1dSRodney W. Grimes 	struct vnode *lowervp;
153df8bae1dSRodney W. Grimes 	struct vnode **vpp;
154df8bae1dSRodney W. Grimes {
1558da80660SBoris Popov 	struct proc *p = curproc;	/* XXX */
156996c772fSJohn Dyson 	struct null_node_hashhead *hd;
157df8bae1dSRodney W. Grimes 	struct null_node *xp;
158df8bae1dSRodney W. Grimes 	struct vnode *othervp, *vp;
159df8bae1dSRodney W. Grimes 	int error;
160df8bae1dSRodney W. Grimes 
1612f9bae59SDavid Greenman 	/*
1622f9bae59SDavid Greenman 	 * Do the MALLOC before the getnewvnode since doing so afterward
1632f9bae59SDavid Greenman 	 * might cause a bogus v_data pointer to get dereferenced
1642f9bae59SDavid Greenman 	 * elsewhere if MALLOC should block.
1652f9bae59SDavid Greenman 	 */
1668da80660SBoris Popov 	MALLOC(xp, struct null_node *, sizeof(struct null_node),
1678da80660SBoris Popov 	    M_NULLFSNODE, M_WAITOK);
1682f9bae59SDavid Greenman 
169623ae52eSPoul-Henning Kamp 	error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp);
1702f9bae59SDavid Greenman 	if (error) {
1718da80660SBoris Popov 		FREE(xp, M_NULLFSNODE);
172df8bae1dSRodney W. Grimes 		return (error);
1732f9bae59SDavid Greenman 	}
174df8bae1dSRodney W. Grimes 	vp = *vpp;
175df8bae1dSRodney W. Grimes 
176df8bae1dSRodney W. Grimes 	vp->v_type = lowervp->v_type;
177df8bae1dSRodney W. Grimes 	xp->null_vnode = vp;
178df8bae1dSRodney W. Grimes 	vp->v_data = xp;
179df8bae1dSRodney W. Grimes 	xp->null_lowervp = lowervp;
180df8bae1dSRodney W. Grimes 	/*
181df8bae1dSRodney W. Grimes 	 * Before we insert our new node onto the hash chains,
182df8bae1dSRodney W. Grimes 	 * check to see if someone else has beaten us to it.
183df8bae1dSRodney W. Grimes 	 * (We could have slept in MALLOC.)
184df8bae1dSRodney W. Grimes 	 */
1852e52c1f9SBruce Evans 	othervp = null_node_find(mp, lowervp);
186623ae52eSPoul-Henning Kamp 	if (othervp) {
1878da80660SBoris Popov 		FREE(xp, M_NULLFSNODE);
188df8bae1dSRodney W. Grimes 		vp->v_type = VBAD;	/* node is discarded */
189df8bae1dSRodney W. Grimes 		vp->v_usecount = 0;	/* XXX */
190df8bae1dSRodney W. Grimes 		*vpp = othervp;
191df8bae1dSRodney W. Grimes 		return 0;
192df8bae1dSRodney W. Grimes 	};
1938da80660SBoris Popov 	lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p);
194df8bae1dSRodney W. Grimes 	VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
195996c772fSJohn Dyson 	hd = NULL_NHASH(lowervp);
196996c772fSJohn Dyson 	LIST_INSERT_HEAD(hd, xp, null_hash);
1978da80660SBoris Popov 	lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
198df8bae1dSRodney W. Grimes 	return 0;
199df8bae1dSRodney W. Grimes }
200df8bae1dSRodney W. Grimes 
201df8bae1dSRodney W. Grimes 
202df8bae1dSRodney W. Grimes /*
203df8bae1dSRodney W. Grimes  * Try to find an existing null_node vnode refering
204df8bae1dSRodney W. Grimes  * to it, otherwise make a new null_node vnode which
205df8bae1dSRodney W. Grimes  * contains a reference to the lower vnode.
206df8bae1dSRodney W. Grimes  */
207df8bae1dSRodney W. Grimes int
208df8bae1dSRodney W. Grimes null_node_create(mp, lowervp, newvpp)
209df8bae1dSRodney W. Grimes 	struct mount *mp;
210df8bae1dSRodney W. Grimes 	struct vnode *lowervp;
211df8bae1dSRodney W. Grimes 	struct vnode **newvpp;
212df8bae1dSRodney W. Grimes {
213df8bae1dSRodney W. Grimes 	struct vnode *aliasvp;
214df8bae1dSRodney W. Grimes 
215623ae52eSPoul-Henning Kamp 	aliasvp = null_node_find(mp, lowervp);
216623ae52eSPoul-Henning Kamp 	if (aliasvp) {
217df8bae1dSRodney W. Grimes 		/*
218df8bae1dSRodney W. Grimes 		 * null_node_find has taken another reference
219df8bae1dSRodney W. Grimes 		 * to the alias vnode.
220df8bae1dSRodney W. Grimes 		 */
2218da80660SBoris Popov #ifdef NULLFS_DEBUG
222e958d078SKATO Takenori 		vprint("null_node_create: exists", aliasvp);
223df8bae1dSRodney W. Grimes #endif
224df8bae1dSRodney W. Grimes 		/* VREF(aliasvp); --- done in null_node_find */
225df8bae1dSRodney W. Grimes 	} else {
226df8bae1dSRodney W. Grimes 		int error;
227df8bae1dSRodney W. Grimes 
228df8bae1dSRodney W. Grimes 		/*
229df8bae1dSRodney W. Grimes 		 * Get new vnode.
230df8bae1dSRodney W. Grimes 		 */
2318da80660SBoris Popov 		NULLFSDEBUG("null_node_create: create new alias vnode\n");
232df8bae1dSRodney W. Grimes 
233df8bae1dSRodney W. Grimes 		/*
234df8bae1dSRodney W. Grimes 		 * Make new vnode reference the null_node.
235df8bae1dSRodney W. Grimes 		 */
236623ae52eSPoul-Henning Kamp 		error = null_node_alloc(mp, lowervp, &aliasvp);
237623ae52eSPoul-Henning Kamp 		if (error)
238df8bae1dSRodney W. Grimes 			return error;
239df8bae1dSRodney W. Grimes 
240df8bae1dSRodney W. Grimes 		/*
241df8bae1dSRodney W. Grimes 		 * aliasvp is already VREF'd by getnewvnode()
242df8bae1dSRodney W. Grimes 		 */
243df8bae1dSRodney W. Grimes 	}
244df8bae1dSRodney W. Grimes 
245df8bae1dSRodney W. Grimes 	vrele(lowervp);
246df8bae1dSRodney W. Grimes 
247df8bae1dSRodney W. Grimes #ifdef DIAGNOSTIC
248df8bae1dSRodney W. Grimes 	if (lowervp->v_usecount < 1) {
249df8bae1dSRodney W. Grimes 		/* Should never happen... */
250623ae52eSPoul-Henning Kamp 		vprint ("null_node_create: alias ", aliasvp);
251623ae52eSPoul-Henning Kamp 		vprint ("null_node_create: lower ", lowervp);
252df8bae1dSRodney W. Grimes 		panic ("null_node_create: lower has 0 usecount.");
253df8bae1dSRodney W. Grimes 	};
254df8bae1dSRodney W. Grimes #endif
255df8bae1dSRodney W. Grimes 
2568da80660SBoris Popov #ifdef NULLFS_DEBUG
257df8bae1dSRodney W. Grimes 	vprint("null_node_create: alias", aliasvp);
258df8bae1dSRodney W. Grimes 	vprint("null_node_create: lower", lowervp);
259df8bae1dSRodney W. Grimes #endif
260df8bae1dSRodney W. Grimes 
261df8bae1dSRodney W. Grimes 	*newvpp = aliasvp;
262df8bae1dSRodney W. Grimes 	return (0);
263df8bae1dSRodney W. Grimes }
264e958d078SKATO Takenori 
265a0f40f54SBruce Evans #ifdef DIAGNOSTIC
2661bf978ceSKATO Takenori #include "opt_ddb.h"
2671bf978ceSKATO Takenori 
268e958d078SKATO Takenori #ifdef DDB
269e958d078SKATO Takenori #define	null_checkvp_barrier	1
270e958d078SKATO Takenori #else
271e958d078SKATO Takenori #define	null_checkvp_barrier	0
272e958d078SKATO Takenori #endif
273e958d078SKATO Takenori 
274df8bae1dSRodney W. Grimes struct vnode *
275df8bae1dSRodney W. Grimes null_checkvp(vp, fil, lno)
276df8bae1dSRodney W. Grimes 	struct vnode *vp;
277df8bae1dSRodney W. Grimes 	char *fil;
278df8bae1dSRodney W. Grimes 	int lno;
279df8bae1dSRodney W. Grimes {
280df8bae1dSRodney W. Grimes 	struct null_node *a = VTONULL(vp);
281df8bae1dSRodney W. Grimes #ifdef notyet
282df8bae1dSRodney W. Grimes 	/*
283df8bae1dSRodney W. Grimes 	 * Can't do this check because vop_reclaim runs
284df8bae1dSRodney W. Grimes 	 * with a funny vop vector.
285df8bae1dSRodney W. Grimes 	 */
286df8bae1dSRodney W. Grimes 	if (vp->v_op != null_vnodeop_p) {
287df8bae1dSRodney W. Grimes 		printf ("null_checkvp: on non-null-node\n");
288df8bae1dSRodney W. Grimes 		while (null_checkvp_barrier) /*WAIT*/ ;
289df8bae1dSRodney W. Grimes 		panic("null_checkvp");
290df8bae1dSRodney W. Grimes 	};
291df8bae1dSRodney W. Grimes #endif
292c5e17d9eSKATO Takenori 	if (a->null_lowervp == NULLVP) {
293df8bae1dSRodney W. Grimes 		/* Should never happen */
294df8bae1dSRodney W. Grimes 		int i; u_long *p;
29589785a16SBruce Evans 		printf("vp = %p, ZERO ptr\n", (void *)vp);
296df8bae1dSRodney W. Grimes 		for (p = (u_long *) a, i = 0; i < 8; i++)
29789785a16SBruce Evans 			printf(" %lx", p[i]);
298df8bae1dSRodney W. Grimes 		printf("\n");
299df8bae1dSRodney W. Grimes 		/* wait for debugger */
300df8bae1dSRodney W. Grimes 		while (null_checkvp_barrier) /*WAIT*/ ;
301df8bae1dSRodney W. Grimes 		panic("null_checkvp");
302df8bae1dSRodney W. Grimes 	}
303df8bae1dSRodney W. Grimes 	if (a->null_lowervp->v_usecount < 1) {
304df8bae1dSRodney W. Grimes 		int i; u_long *p;
30589785a16SBruce Evans 		printf("vp = %p, unref'ed lowervp\n", (void *)vp);
306df8bae1dSRodney W. Grimes 		for (p = (u_long *) a, i = 0; i < 8; i++)
30789785a16SBruce Evans 			printf(" %lx", p[i]);
308df8bae1dSRodney W. Grimes 		printf("\n");
309df8bae1dSRodney W. Grimes 		/* wait for debugger */
310df8bae1dSRodney W. Grimes 		while (null_checkvp_barrier) /*WAIT*/ ;
311df8bae1dSRodney W. Grimes 		panic ("null with unref'ed lowervp");
312df8bae1dSRodney W. Grimes 	};
313df8bae1dSRodney W. Grimes #ifdef notyet
314df8bae1dSRodney W. Grimes 	printf("null %x/%d -> %x/%d [%s, %d]\n",
315df8bae1dSRodney W. Grimes 	        NULLTOV(a), NULLTOV(a)->v_usecount,
316df8bae1dSRodney W. Grimes 		a->null_lowervp, a->null_lowervp->v_usecount,
317df8bae1dSRodney W. Grimes 		fil, lno);
318df8bae1dSRodney W. Grimes #endif
319df8bae1dSRodney W. Grimes 	return a->null_lowervp;
320df8bae1dSRodney W. Grimes }
321df8bae1dSRodney W. Grimes #endif
322