null_subr.c revision 65467
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: head/sys/fs/nullfs/null_subr.c 65467 2000-09-05 09:02:07Z bp $
39 */
40
41#include <sys/param.h>
42#include <sys/systm.h>
43#include <sys/kernel.h>
44#include <sys/proc.h>
45#include <sys/vnode.h>
46#include <sys/mount.h>
47#include <sys/malloc.h>
48#include <miscfs/nullfs/null.h>
49
50#define LOG2_SIZEVNODE 7		/* log2(sizeof struct vnode) */
51#define	NNULLNODECACHE 16
52
53/*
54 * Null layer cache:
55 * Each cache entry holds a reference to the lower vnode
56 * along with a pointer to the alias vnode.  When an
57 * entry is added the lower vnode is VREF'd.  When the
58 * alias is removed the lower vnode is vrele'd.
59 */
60
61#define	NULL_NHASH(vp) \
62	(&null_node_hashtbl[(((uintptr_t)vp)>>LOG2_SIZEVNODE) & null_node_hash])
63
64static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
65static u_long null_node_hash;
66struct lock null_hashlock;
67
68static MALLOC_DEFINE(M_NULLFSHASH, "NULLFS hash", "NULLFS hash table");
69MALLOC_DEFINE(M_NULLFSNODE, "NULLFS node", "NULLFS vnode private part");
70
71static int	null_node_alloc(struct mount *mp, struct vnode *lowervp,
72				     struct vnode **vpp);
73static struct vnode *
74		null_node_find(struct mount *mp, struct vnode *lowervp);
75
76/*
77 * Initialise cache headers
78 */
79int
80nullfs_init(vfsp)
81	struct vfsconf *vfsp;
82{
83
84	NULLFSDEBUG("nullfs_init\n");		/* printed during system boot */
85	null_node_hashtbl = hashinit(NNULLNODECACHE, M_NULLFSHASH, &null_node_hash);
86	lockinit(&null_hashlock, PVFS, "nullhs", 0, 0);
87	return (0);
88}
89
90int
91nullfs_uninit(vfsp)
92	struct vfsconf *vfsp;
93{
94
95        if (null_node_hashtbl)
96		free(null_node_hashtbl, M_NULLFSHASH);
97	return (0);
98}
99
100/*
101 * Return a VREF'ed alias for lower vnode if already exists, else 0.
102 */
103static struct vnode *
104null_node_find(mp, lowervp)
105	struct mount *mp;
106	struct vnode *lowervp;
107{
108	struct proc *p = curproc;	/* XXX */
109	struct null_node_hashhead *hd;
110	struct null_node *a;
111	struct vnode *vp;
112
113	/*
114	 * Find hash base, and then search the (two-way) linked
115	 * list looking for a null_node structure which is referencing
116	 * the lower vnode.  If found, the increment the null_node
117	 * reference count (but NOT the lower vnode's VREF counter).
118	 */
119	hd = NULL_NHASH(lowervp);
120loop:
121	lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p);
122	for (a = hd->lh_first; a != 0; a = a->null_hash.le_next) {
123		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
124			vp = NULLTOV(a);
125			lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
126			/*
127			 * We need vget for the VXLOCK
128			 * stuff, but we don't want to lock
129			 * the lower node.
130			 */
131			if (vget(vp, 0, p)) {
132				printf ("null_node_find: vget failed.\n");
133				goto loop;
134			};
135			return (vp);
136		}
137	}
138	lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
139
140	return NULLVP;
141}
142
143
144/*
145 * Make a new null_node node.
146 * Vp is the alias vnode, lofsvp is the lower vnode.
147 * Maintain a reference to (lowervp).
148 */
149static int
150null_node_alloc(mp, lowervp, vpp)
151	struct mount *mp;
152	struct vnode *lowervp;
153	struct vnode **vpp;
154{
155	struct proc *p = curproc;	/* XXX */
156	struct null_node_hashhead *hd;
157	struct null_node *xp;
158	struct vnode *othervp, *vp;
159	int error;
160
161	/*
162	 * Do the MALLOC before the getnewvnode since doing so afterward
163	 * might cause a bogus v_data pointer to get dereferenced
164	 * elsewhere if MALLOC should block.
165	 */
166	MALLOC(xp, struct null_node *, sizeof(struct null_node),
167	    M_NULLFSNODE, M_WAITOK);
168
169	error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp);
170	if (error) {
171		FREE(xp, M_NULLFSNODE);
172		return (error);
173	}
174	vp = *vpp;
175
176	vp->v_type = lowervp->v_type;
177	xp->null_vnode = vp;
178	vp->v_data = xp;
179	xp->null_lowervp = lowervp;
180	/*
181	 * Before we insert our new node onto the hash chains,
182	 * check to see if someone else has beaten us to it.
183	 * (We could have slept in MALLOC.)
184	 */
185	othervp = null_node_find(mp, lowervp);
186	if (othervp) {
187		FREE(xp, M_NULLFSNODE);
188		vp->v_type = VBAD;	/* node is discarded */
189		vp->v_usecount = 0;	/* XXX */
190		*vpp = othervp;
191		return 0;
192	};
193	lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p);
194	VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
195	hd = NULL_NHASH(lowervp);
196	LIST_INSERT_HEAD(hd, xp, null_hash);
197	lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
198	return 0;
199}
200
201
202/*
203 * Try to find an existing null_node vnode refering
204 * to it, otherwise make a new null_node vnode which
205 * contains a reference to the lower vnode.
206 */
207int
208null_node_create(mp, lowervp, newvpp)
209	struct mount *mp;
210	struct vnode *lowervp;
211	struct vnode **newvpp;
212{
213	struct vnode *aliasvp;
214
215	aliasvp = null_node_find(mp, lowervp);
216	if (aliasvp) {
217		/*
218		 * null_node_find has taken another reference
219		 * to the alias vnode.
220		 */
221#ifdef NULLFS_DEBUG
222		vprint("null_node_create: exists", aliasvp);
223#endif
224		/* VREF(aliasvp); --- done in null_node_find */
225	} else {
226		int error;
227
228		/*
229		 * Get new vnode.
230		 */
231		NULLFSDEBUG("null_node_create: create new alias vnode\n");
232
233		/*
234		 * Make new vnode reference the null_node.
235		 */
236		error = null_node_alloc(mp, lowervp, &aliasvp);
237		if (error)
238			return error;
239
240		/*
241		 * aliasvp is already VREF'd by getnewvnode()
242		 */
243	}
244
245	vrele(lowervp);
246
247#ifdef DIAGNOSTIC
248	if (lowervp->v_usecount < 1) {
249		/* Should never happen... */
250		vprint ("null_node_create: alias ", aliasvp);
251		vprint ("null_node_create: lower ", lowervp);
252		panic ("null_node_create: lower has 0 usecount.");
253	};
254#endif
255
256#ifdef NULLFS_DEBUG
257	vprint("null_node_create: alias", aliasvp);
258	vprint("null_node_create: lower", lowervp);
259#endif
260
261	*newvpp = aliasvp;
262	return (0);
263}
264
265#ifdef DIAGNOSTIC
266#include "opt_ddb.h"
267
268#ifdef DDB
269#define	null_checkvp_barrier	1
270#else
271#define	null_checkvp_barrier	0
272#endif
273
274struct vnode *
275null_checkvp(vp, fil, lno)
276	struct vnode *vp;
277	char *fil;
278	int lno;
279{
280	struct null_node *a = VTONULL(vp);
281#ifdef notyet
282	/*
283	 * Can't do this check because vop_reclaim runs
284	 * with a funny vop vector.
285	 */
286	if (vp->v_op != null_vnodeop_p) {
287		printf ("null_checkvp: on non-null-node\n");
288		while (null_checkvp_barrier) /*WAIT*/ ;
289		panic("null_checkvp");
290	};
291#endif
292	if (a->null_lowervp == NULLVP) {
293		/* Should never happen */
294		int i; u_long *p;
295		printf("vp = %p, ZERO ptr\n", (void *)vp);
296		for (p = (u_long *) a, i = 0; i < 8; i++)
297			printf(" %lx", p[i]);
298		printf("\n");
299		/* wait for debugger */
300		while (null_checkvp_barrier) /*WAIT*/ ;
301		panic("null_checkvp");
302	}
303	if (a->null_lowervp->v_usecount < 1) {
304		int i; u_long *p;
305		printf("vp = %p, unref'ed lowervp\n", (void *)vp);
306		for (p = (u_long *) a, i = 0; i < 8; i++)
307			printf(" %lx", p[i]);
308		printf("\n");
309		/* wait for debugger */
310		while (null_checkvp_barrier) /*WAIT*/ ;
311		panic ("null with unref'ed lowervp");
312	};
313#ifdef notyet
314	printf("null %x/%d -> %x/%d [%s, %d]\n",
315	        NULLTOV(a), NULLTOV(a)->v_usecount,
316		a->null_lowervp, a->null_lowervp->v_usecount,
317		fil, lno);
318#endif
319	return a->null_lowervp;
320}
321#endif
322