xref: /freebsd/sys/fs/nullfs/null_subr.c (revision daf1cffce2e07931f27c6c6998652e90df6ba87e)
1 /*
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software donated to Berkeley by
6  * Jan-Simon Pendry.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *	This product includes software developed by the University of
19  *	California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  *
36  *	@(#)null_subr.c	8.7 (Berkeley) 5/14/95
37  *
38  * $FreeBSD$
39  */
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/proc.h>
44 #include <sys/vnode.h>
45 #include <sys/mount.h>
46 #include <sys/malloc.h>
47 #include <miscfs/nullfs/null.h>
48 
49 #define LOG2_SIZEVNODE 7		/* log2(sizeof struct vnode) */
50 #define	NNULLNODECACHE 16
51 
52 /*
53  * Null layer cache:
54  * Each cache entry holds a reference to the lower vnode
55  * along with a pointer to the alias vnode.  When an
56  * entry is added the lower vnode is VREF'd.  When the
57  * alias is removed the lower vnode is vrele'd.
58  */
59 
60 #define	NULL_NHASH(vp) \
61 	(&null_node_hashtbl[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & null_node_hash])
62 static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
63 static u_long null_node_hash;
64 
65 static int	null_node_alloc __P((struct mount *mp, struct vnode *lowervp,
66 				     struct vnode **vpp));
67 static struct vnode *
68 		null_node_find __P((struct mount *mp, struct vnode *lowervp));
69 
70 /*
71  * Initialise cache headers
72  */
73 int
74 nullfs_init(vfsp)
75 	struct vfsconf *vfsp;
76 {
77 
78 #ifdef DEBUG
79 	printf("nullfs_init\n");		/* printed during system boot */
80 #endif
81 	null_node_hashtbl = hashinit(NNULLNODECACHE, M_CACHE, &null_node_hash);
82 	return (0);
83 }
84 
85 /*
86  * Return a VREF'ed alias for lower vnode if already exists, else 0.
87  */
88 static struct vnode *
89 null_node_find(mp, lowervp)
90 	struct mount *mp;
91 	struct vnode *lowervp;
92 {
93 	struct proc *p = curproc;	/* XXX */
94 	struct null_node_hashhead *hd;
95 	struct null_node *a;
96 	struct vnode *vp;
97 
98 	/*
99 	 * Find hash base, and then search the (two-way) linked
100 	 * list looking for a null_node structure which is referencing
101 	 * the lower vnode.  If found, the increment the null_node
102 	 * reference count (but NOT the lower vnode's VREF counter).
103 	 */
104 	hd = NULL_NHASH(lowervp);
105 loop:
106 	for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) {
107 		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
108 			vp = NULLTOV(a);
109 			/*
110 			 * We need vget for the VXLOCK
111 			 * stuff, but we don't want to lock
112 			 * the lower node.
113 			 */
114 			if (vget(vp, 0, p)) {
115 				printf ("null_node_find: vget failed.\n");
116 				goto loop;
117 			};
118 			return (vp);
119 		}
120 	}
121 
122 	return NULLVP;
123 }
124 
125 
126 /*
127  * Make a new null_node node.
128  * Vp is the alias vnode, lofsvp is the lower vnode.
129  * Maintain a reference to (lowervp).
130  */
131 static int
132 null_node_alloc(mp, lowervp, vpp)
133 	struct mount *mp;
134 	struct vnode *lowervp;
135 	struct vnode **vpp;
136 {
137 	struct null_node_hashhead *hd;
138 	struct null_node *xp;
139 	struct vnode *othervp, *vp;
140 	int error;
141 
142 	/*
143 	 * Do the MALLOC before the getnewvnode since doing so afterward
144 	 * might cause a bogus v_data pointer to get dereferenced
145 	 * elsewhere if MALLOC should block.
146 	 */
147 	MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
148 
149 	error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp);
150 	if (error) {
151 		FREE(xp, M_TEMP);
152 		return (error);
153 	}
154 	vp = *vpp;
155 
156 	vp->v_type = lowervp->v_type;
157 	xp->null_vnode = vp;
158 	vp->v_data = xp;
159 	xp->null_lowervp = lowervp;
160 	/*
161 	 * Before we insert our new node onto the hash chains,
162 	 * check to see if someone else has beaten us to it.
163 	 * (We could have slept in MALLOC.)
164 	 */
165 	othervp = null_node_find(mp, lowervp);
166 	if (othervp) {
167 		FREE(xp, M_TEMP);
168 		vp->v_type = VBAD;	/* node is discarded */
169 		vp->v_usecount = 0;	/* XXX */
170 		*vpp = othervp;
171 		return 0;
172 	};
173 	VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
174 	hd = NULL_NHASH(lowervp);
175 	LIST_INSERT_HEAD(hd, xp, null_hash);
176 	return 0;
177 }
178 
179 
180 /*
181  * Try to find an existing null_node vnode refering
182  * to it, otherwise make a new null_node vnode which
183  * contains a reference to the lower vnode.
184  */
185 int
186 null_node_create(mp, lowervp, newvpp)
187 	struct mount *mp;
188 	struct vnode *lowervp;
189 	struct vnode **newvpp;
190 {
191 	struct vnode *aliasvp;
192 
193 	aliasvp = null_node_find(mp, lowervp);
194 	if (aliasvp) {
195 		/*
196 		 * null_node_find has taken another reference
197 		 * to the alias vnode.
198 		 */
199 #ifdef DEBUG
200 		vprint("null_node_create: exists", aliasvp);
201 #endif
202 		/* VREF(aliasvp); --- done in null_node_find */
203 	} else {
204 		int error;
205 
206 		/*
207 		 * Get new vnode.
208 		 */
209 #ifdef DEBUG
210 		printf("null_node_create: create new alias vnode\n");
211 #endif
212 
213 		/*
214 		 * Make new vnode reference the null_node.
215 		 */
216 		error = null_node_alloc(mp, lowervp, &aliasvp);
217 		if (error)
218 			return error;
219 
220 		/*
221 		 * aliasvp is already VREF'd by getnewvnode()
222 		 */
223 	}
224 
225 	vrele(lowervp);
226 
227 #ifdef DIAGNOSTIC
228 	if (lowervp->v_usecount < 1) {
229 		/* Should never happen... */
230 		vprint ("null_node_create: alias ", aliasvp);
231 		vprint ("null_node_create: lower ", lowervp);
232 		panic ("null_node_create: lower has 0 usecount.");
233 	};
234 #endif
235 
236 #ifdef DEBUG
237 	vprint("null_node_create: alias", aliasvp);
238 	vprint("null_node_create: lower", lowervp);
239 #endif
240 
241 	*newvpp = aliasvp;
242 	return (0);
243 }
244 
245 #ifdef DIAGNOSTIC
246 #include "opt_ddb.h"
247 
248 #ifdef DDB
249 #define	null_checkvp_barrier	1
250 #else
251 #define	null_checkvp_barrier	0
252 #endif
253 
254 struct vnode *
255 null_checkvp(vp, fil, lno)
256 	struct vnode *vp;
257 	char *fil;
258 	int lno;
259 {
260 	struct null_node *a = VTONULL(vp);
261 #ifdef notyet
262 	/*
263 	 * Can't do this check because vop_reclaim runs
264 	 * with a funny vop vector.
265 	 */
266 	if (vp->v_op != null_vnodeop_p) {
267 		printf ("null_checkvp: on non-null-node\n");
268 		while (null_checkvp_barrier) /*WAIT*/ ;
269 		panic("null_checkvp");
270 	};
271 #endif
272 	if (a->null_lowervp == NULLVP) {
273 		/* Should never happen */
274 		int i; u_long *p;
275 		printf("vp = %p, ZERO ptr\n", (void *)vp);
276 		for (p = (u_long *) a, i = 0; i < 8; i++)
277 			printf(" %lx", p[i]);
278 		printf("\n");
279 		/* wait for debugger */
280 		while (null_checkvp_barrier) /*WAIT*/ ;
281 		panic("null_checkvp");
282 	}
283 	if (a->null_lowervp->v_usecount < 1) {
284 		int i; u_long *p;
285 		printf("vp = %p, unref'ed lowervp\n", (void *)vp);
286 		for (p = (u_long *) a, i = 0; i < 8; i++)
287 			printf(" %lx", p[i]);
288 		printf("\n");
289 		/* wait for debugger */
290 		while (null_checkvp_barrier) /*WAIT*/ ;
291 		panic ("null with unref'ed lowervp");
292 	};
293 #ifdef notyet
294 	printf("null %x/%d -> %x/%d [%s, %d]\n",
295 	        NULLTOV(a), NULLTOV(a)->v_usecount,
296 		a->null_lowervp, a->null_lowervp->v_usecount,
297 		fil, lno);
298 #endif
299 	return a->null_lowervp;
300 }
301 #endif
302