xref: /csrg-svn/sys/miscfs/nullfs/null_subr.c (revision 54944)
1 /*
2  * Copyright (c) 1992 The Regents of the University of California
3  * Copyright (c) 1990, 1992 Jan-Simon Pendry
4  * All rights reserved.
5  *
6  * This code is derived from software donated to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)lofs_subr.c	1.2 (Berkeley) 6/18/92
12  *
13  * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
14  */
15 
16 #include <sys/param.h>
17 #include <sys/systm.h>
18 #include <sys/time.h>
19 #include <sys/types.h>
20 #include <sys/vnode.h>
21 #include <sys/mount.h>
22 #include <sys/namei.h>
23 #include <sys/malloc.h>
24 #include <nullfs/null.h>
25 
26 #define LOG2_SIZEVNODE 7		/* log2(sizeof struct vnode) */
27 #define	NNULLNODECACHE 16
28 #define	NULL_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NNULLNODECACHE-1))
29 
30 /*
31  * Null layer cache:
32  * Each cache entry holds a reference to the lower vnode
33  * along with a pointer to the alias vnode.  When an
34  * entry is added the lower vnode is VREF'd.  When the
35  * alias is removed the lower vnode is vrele'd.
36  */
37 
38 /*
39  * Cache head
40  */
41 struct null_node_cache {
42 	struct null_node	*ac_forw;
43 	struct null_node	*ac_back;
44 };
45 
46 static struct null_node_cache null_node_cache[NNULLNODECACHE];
47 
48 /*
49  * Initialise cache headers
50  */
51 nullfs_init()
52 {
53 	struct null_node_cache *ac;
54 #ifdef NULLFS_DIAGNOSTIC
55 	printf("nullfs_init\n");		/* printed during system boot */
56 #endif
57 
58 	for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++)
59 		ac->ac_forw = ac->ac_back = (struct null_node *) ac;
60 }
61 
62 /*
63  * Compute hash list for given lower vnode
64  */
65 static struct null_node_cache *
66 null_node_hash(lowervp)
67 struct vnode *lowervp;
68 {
69 	return (&null_node_cache[NULL_NHASH(lowervp)]);
70 }
71 
72 /*
73  * Make a new null_node node.
74  * Vp is the alias vnode, lofsvp is the lower vnode.
75  * Maintain a reference to (lowervp).
76  */
77 static void
78 null_node_alloc(vp, lowervp)
79 	struct vnode *vp;
80 	struct vnode *lowervp;
81 {
82 	struct null_node_cache *hd;
83 	struct null_node *a;
84 
85 #ifdef NULLFS_DIAGNOSTIC
86 	printf("null_node_alloc(%x, %x)\n", vp, lowervp);
87 #endif
88 
89 	MALLOC(a, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
90 	vp->v_type = lowervp->v_type;
91 	a->null_vnode = vp;
92 	vp->v_data = a;
93 	VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
94 	a->null_lowervp = lowervp;
95 	hd = null_node_hash(lowervp);
96 	insque(a, hd);
97 
98 #ifdef NULLFS_DIAGNOSTIC
99 	vprint("null_node_alloc vp", vp);
100 	vprint("null_node_alloc lowervp", lowervp);
101 #endif
102 }
103 
104 #ifdef NULLFS_DIAGNOSTIC
105 /*
106  * NEEDSWORK:  The ability to set lowervp to null here
107  * implies that one can never count on lowervp staying null
108  * (even if vp is locked).  This seems quite bad.  Think
109  * about these things.
110  */
111 void
112 null_node_flushmp (mp)
113 	struct mount *mp;
114 {
115 	struct null_node_cache *ac;
116 	int i = 0;
117 	struct null_node *roota;
118 
119 	printf("null_node_flushmp (%x)\n", mp);
120 
121 	roota = VTONULL(MOUNTTONULLMOUNT(mp)->nullm_rootvp);
122 
123 	for (ac = null_node_cache; ac < null_node_cache + NNULLNODECACHE; ac++) {
124 		struct null_node *a = ac->ac_forw;
125 		while (a != (struct null_node *) ac) {
126 			if (a != roota && NULLTOV(a)->v_mount == mp) {
127 				struct vnode *vp = a->null_lowervp;
128 				if (vp) {
129 					a->null_lowervp = 0;
130 					vprint("null_flushmp: would vrele", vp);
131 					/*vrele(vp);*/
132 					i++;
133 				}
134 			}
135 			a = a->null_forw;
136 		}
137 	}
138 	if (i > 0)
139 		printf("null_node: vrele'd %d aliases\n", i);
140 }
141 #endif
142 
143 /*
144  * XXX - this should go elsewhere.
145  * Just like vget, but with no lock at the end.
146  */
147 int
148 vget_nolock(vp)
149 	register struct vnode *vp;
150 {
151 	extern struct vnode *vfreeh, **vfreet;
152 	register struct vnode *vq;
153 
154 	if (vp->v_flag & VXLOCK) {
155 		vp->v_flag |= VXWANT;
156 		sleep((caddr_t)vp, PINOD);
157 		return (1);
158 	}
159 	if (vp->v_usecount == 0) {
160 		if (vq = vp->v_freef)
161 			vq->v_freeb = vp->v_freeb;
162 		else
163 			vfreet = vp->v_freeb;
164 		*vp->v_freeb = vq;
165 		vp->v_freef = NULL;
166 		vp->v_freeb = NULL;
167 	}
168 	VREF(vp);
169 	/* VOP_LOCK(vp); */
170 	return (0);
171 }
172 
173 
174 /*
175  * Return a VREF'ed alias for lower vnode if already exists, else 0.
176  */
177 static struct vnode *
178 null_node_find(mp, lowervp)
179 	struct mount *mp;
180 	struct vnode *lowervp;
181 {
182 	struct null_node_cache *hd;
183 	struct null_node *a;
184 	struct vnode *vp;
185 
186 #ifdef NULLFS_DIAGNOSTIC
187 	printf("null_node_find(mp = %x, lower = %x)\n", mp, lowervp);
188 #endif
189 
190 	/*
191 	 * Find hash base, and then search the (two-way) linked
192 	 * list looking for a null_node structure which is referencing
193 	 * the lower vnode.  If found, the increment the null_node
194 	 * reference count (but NOT the lower vnode's VREF counter).
195 	 */
196 	hd = null_node_hash(lowervp);
197 loop:
198 	for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
199 		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
200 #ifdef NULLFS_DIAGNOSTIC
201 			printf("null_node_find(%x): found (%x,%x)->%x\n",
202 				lowervp, mp, NULLTOV(a), lowervp);
203 #endif
204 			vp = NULLTOV(a);
205 			/*
206 			 * NEEDSWORK: Don't call the normal vget,
207 			 * it will do a VOP_LOCK which is bypassed
208 			 * and will lock against ourselves.
209 			 * Unfortunately, we need vget for the VXLOCK
210 			 * stuff.
211 			 */
212 			if (vget_nolock(vp)) {
213 				printf ("null_node_find: vget failed.\n");
214 				goto loop;
215 			};
216 			a->null_isinactive = 0;
217 			return (vp);
218 		}
219 	}
220 
221 #ifdef NULLFS_DIAGNOSTIC
222 	printf("null_node_find(%x, %x): NOT found\n", mp, lowervp);
223 #endif
224 
225 	return NULL;
226 }
227 
228 #if 1
229 int null_node_create_barrier = 1;
230 #endif
231 
232 /*
233  * Try to find an existing null_node vnode refering
234  * to it, otherwise make a new null_node vnode which
235  * contains a reference to the lower vnode.
236  */
237 int
238 null_node_create(mp, lowervp, newvpp)
239 	struct mount *mp;
240 	struct vnode *lowervp;
241 	struct vnode **newvpp;
242 {
243 	struct vnode *aliasvp;
244 
245 	if (aliasvp = null_node_find(mp, lowervp)) {
246 		/*
247 		 * null_node_find has taken another reference
248 		 * to the alias vnode.
249 		 */
250 #ifdef NULLFS_DIAGNOSTIC
251 		vprint("null_node_create: exists", NULLTOV(ap));
252 #endif
253 		/* VREF(aliasvp); --- done in null_node_find */
254 	} else {
255 		int error;
256 
257 		/*
258 		 * Get new vnode.
259 		 */
260 #ifdef NULLFS_DIAGNOSTIC
261 		printf("null_node_create: create new alias vnode\n");
262 #endif
263 		if (error = getnewvnode(VT_UFS, mp, null_vnodeop_p, &aliasvp))
264 			return (error);	/* XXX: VT_NULL above */
265 
266 		/*
267 		 * Make new vnode reference the null_node.
268 		 */
269 		null_node_alloc(aliasvp, lowervp);
270 
271 		/*
272 		 * aliasvp is already VREF'd by getnewvnode()
273 		 */
274 	}
275 
276 	vrele(lowervp);
277 
278 	if (lowervp->v_usecount < 1) {
279 		vprint ("null_node_create: alias ");
280 		vprint ("null_node_create: lower ");
281 		printf ("null_node_create: lower has 0 usecount.\n");
282 		while (null_node_create_barrier) /* wait */ ;
283 		panic ("null_node_create: lower has 0 usecount.");
284 	};
285 
286 #ifdef NULLFS_DIAGNOSTIC
287 	vprint("null_node_create: alias", aliasvp);
288 	vprint("null_node_create: lower", lowervp);
289 #endif
290 
291 	*newvpp = aliasvp;
292 	return (0);
293 }
294 #ifdef NULLFS_DIAGNOSTIC
295 int null_checkvp_barrier = 1;
296 struct vnode *
297 null_checkvp(vp, fil, lno)
298 	struct vnode *vp;
299 	char *fil;
300 	int lno;
301 {
302 	struct null_node *a = VTONULL(vp);
303 #if 0
304 	/*
305 	 * Can't do this check because vop_reclaim runs
306 	 * with a funny vop vector.
307 	 */
308 	if (vp->v_op != null_vnodeop_p) {
309 		printf ("null_checkvp: on non-null-node\n");
310 		while (null_checkvp_barrier) /*WAIT*/ ;
311 		panic("null_checkvp");
312 	};
313 #endif
314 	if (a->null_lowervp == NULL) {
315 		/* Should never happen */
316 		int i; u_long *p;
317 		printf("vp = %x, ZERO ptr\n", vp);
318 		for (p = (u_long *) a, i = 0; i < 8; i++)
319 			printf(" %x", p[i]);
320 		printf("\n");
321 		/* wait for debugger */
322 		while (null_checkvp_barrier) /*WAIT*/ ;
323 		panic("null_checkvp");
324 	}
325 	if (a->null_lowervp->v_usecount < 1) {
326 		int i; u_long *p;
327 		printf("vp = %x, unref'ed lowervp\n", vp);
328 		for (p = (u_long *) a, i = 0; i < 8; i++)
329 			printf(" %x", p[i]);
330 		printf("\n");
331 		/* wait for debugger */
332 		while (null_checkvp_barrier) /*WAIT*/ ;
333 		panic ("null with unref'ed lowervp");
334 	};
335 #if 0
336 	printf("null %x/%d -> %x/%d [%s, %d]\n",
337 	        NULLTOV(a), NULLTOV(a)->v_usecount,
338 		a->null_lowervp, a->null_lowervp->v_usecount,
339 		fil, lno);
340 #endif
341 	return a->null_lowervp;
342 }
343 #endif
344