xref: /original-bsd/sys/miscfs/nullfs/null_subr.c (revision 3705696b)
1 /*
2  * Copyright (c) 1992, 1993
3  *	The Regents of the University of California.  All rights reserved.
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  *	@(#)null_subr.c	8.1 (Berkeley) 06/10/93
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 <miscfs/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 
70 	return (&null_node_cache[NULL_NHASH(lowervp)]);
71 }
72 
73 
74 /*
75  * XXX - this should go elsewhere.
76  * Just like vget, but with no lock at the end.
77  */
78 int
79 vget_nolock(vp)
80 	register struct vnode *vp;
81 {
82 	extern struct vnode *vfreeh, **vfreet;
83 	register struct vnode *vq;
84 
85 	if (vp->v_flag & VXLOCK) {
86 		vp->v_flag |= VXWANT;
87 		sleep((caddr_t)vp, PINOD);
88 		return (1);
89 	}
90 	if (vp->v_usecount == 0) {
91 		if (vq = vp->v_freef)
92 			vq->v_freeb = vp->v_freeb;
93 		else
94 			vfreet = vp->v_freeb;
95 		*vp->v_freeb = vq;
96 		vp->v_freef = NULL;
97 		vp->v_freeb = NULL;
98 	}
99 	VREF(vp);
100 	/* VOP_LOCK(vp); */
101 	return (0);
102 }
103 
104 
105 /*
106  * Return a VREF'ed alias for lower vnode if already exists, else 0.
107  */
108 static struct vnode *
109 null_node_find(mp, lowervp)
110 	struct mount *mp;
111 	struct vnode *lowervp;
112 {
113 	struct null_node_cache *hd;
114 	struct null_node *a;
115 	struct vnode *vp;
116 
117 	/*
118 	 * Find hash base, and then search the (two-way) linked
119 	 * list looking for a null_node structure which is referencing
120 	 * the lower vnode.  If found, the increment the null_node
121 	 * reference count (but NOT the lower vnode's VREF counter).
122 	 */
123 	hd = null_node_hash(lowervp);
124 loop:
125 	for (a = hd->ac_forw; a != (struct null_node *) hd; a = a->null_forw) {
126 		if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
127 			vp = NULLTOV(a);
128 			/*
129 			 * We need vget for the VXLOCK
130 			 * stuff, but we don't want to lock
131 			 * the lower node.
132 			 */
133 			if (vget_nolock(vp)) {
134 				printf ("null_node_find: vget failed.\n");
135 				goto loop;
136 			};
137 			return (vp);
138 		}
139 	}
140 
141 	return NULL;
142 }
143 
144 
145 /*
146  * Make a new null_node node.
147  * Vp is the alias vnode, lofsvp is the lower vnode.
148  * Maintain a reference to (lowervp).
149  */
150 static int
151 null_node_alloc(mp, lowervp, vpp)
152 	struct mount *mp;
153 	struct vnode *lowervp;
154 	struct vnode **vpp;
155 {
156 	struct null_node_cache *hd;
157 	struct null_node *xp;
158 	struct vnode *othervp, *vp;
159 	int error;
160 
161 	if (error = getnewvnode(VT_UFS, mp, null_vnodeop_p, vpp))
162 		return (error);	/* XXX: VT_NULL above */
163 	vp = *vpp;
164 
165 	MALLOC(xp, struct null_node *, sizeof(struct null_node), M_TEMP, M_WAITOK);
166 	vp->v_type = lowervp->v_type;
167 	xp->null_vnode = vp;
168 	vp->v_data = xp;
169 	xp->null_lowervp = lowervp;
170 	/*
171 	 * Before we insert our new node onto the hash chains,
172 	 * check to see if someone else has beaten us to it.
173 	 * (We could have slept in MALLOC.)
174 	 */
175 	if (othervp = null_node_find(lowervp)) {
176 		FREE(xp, M_TEMP);
177 		vp->v_type = VBAD;	/* node is discarded */
178 		vp->v_usecount = 0;	/* XXX */
179 		*vpp = othervp;
180 		return 0;
181 	};
182 	VREF(lowervp);   /* Extra VREF will be vrele'd in null_node_create */
183 	hd = null_node_hash(lowervp);
184 	insque(xp, hd);
185 	return 0;
186 }
187 
188 
189 /*
190  * Try to find an existing null_node vnode refering
191  * to it, otherwise make a new null_node vnode which
192  * contains a reference to the lower vnode.
193  */
194 int
195 null_node_create(mp, lowervp, newvpp)
196 	struct mount *mp;
197 	struct vnode *lowervp;
198 	struct vnode **newvpp;
199 {
200 	struct vnode *aliasvp;
201 
202 	if (aliasvp = null_node_find(mp, lowervp)) {
203 		/*
204 		 * null_node_find has taken another reference
205 		 * to the alias vnode.
206 		 */
207 #ifdef NULLFS_DIAGNOSTIC
208 		vprint("null_node_create: exists", NULLTOV(ap));
209 #endif
210 		/* VREF(aliasvp); --- done in null_node_find */
211 	} else {
212 		int error;
213 
214 		/*
215 		 * Get new vnode.
216 		 */
217 #ifdef NULLFS_DIAGNOSTIC
218 		printf("null_node_create: create new alias vnode\n");
219 #endif
220 
221 		/*
222 		 * Make new vnode reference the null_node.
223 		 */
224 		if (error = null_node_alloc(mp, lowervp, &aliasvp))
225 			return error;
226 
227 		/*
228 		 * aliasvp is already VREF'd by getnewvnode()
229 		 */
230 	}
231 
232 	vrele(lowervp);
233 
234 #ifdef DIAGNOSTIC
235 	if (lowervp->v_usecount < 1) {
236 		/* Should never happen... */
237 		vprint ("null_node_create: alias ");
238 		vprint ("null_node_create: lower ");
239 		printf ("null_node_create: lower has 0 usecount.\n");
240 		panic ("null_node_create: lower has 0 usecount.");
241 	};
242 #endif
243 
244 #ifdef NULLFS_DIAGNOSTIC
245 	vprint("null_node_create: alias", aliasvp);
246 	vprint("null_node_create: lower", lowervp);
247 #endif
248 
249 	*newvpp = aliasvp;
250 	return (0);
251 }
252 #ifdef NULLFS_DIAGNOSTIC
253 struct vnode *
254 null_checkvp(vp, fil, lno)
255 	struct vnode *vp;
256 	char *fil;
257 	int lno;
258 {
259 	struct null_node *a = VTONULL(vp);
260 #ifdef notyet
261 	/*
262 	 * Can't do this check because vop_reclaim runs
263 	 * with a funny vop vector.
264 	 */
265 	if (vp->v_op != null_vnodeop_p) {
266 		printf ("null_checkvp: on non-null-node\n");
267 		while (null_checkvp_barrier) /*WAIT*/ ;
268 		panic("null_checkvp");
269 	};
270 #endif
271 	if (a->null_lowervp == NULL) {
272 		/* Should never happen */
273 		int i; u_long *p;
274 		printf("vp = %x, ZERO ptr\n", vp);
275 		for (p = (u_long *) a, i = 0; i < 8; i++)
276 			printf(" %x", p[i]);
277 		printf("\n");
278 		/* wait for debugger */
279 		while (null_checkvp_barrier) /*WAIT*/ ;
280 		panic("null_checkvp");
281 	}
282 	if (a->null_lowervp->v_usecount < 1) {
283 		int i; u_long *p;
284 		printf("vp = %x, unref'ed lowervp\n", vp);
285 		for (p = (u_long *) a, i = 0; i < 8; i++)
286 			printf(" %x", p[i]);
287 		printf("\n");
288 		/* wait for debugger */
289 		while (null_checkvp_barrier) /*WAIT*/ ;
290 		panic ("null with unref'ed lowervp");
291 	};
292 #ifdef notyet
293 	printf("null %x/%d -> %x/%d [%s, %d]\n",
294 	        NULLTOV(a), NULLTOV(a)->v_usecount,
295 		a->null_lowervp, a->null_lowervp->v_usecount,
296 		fil, lno);
297 #endif
298 	return a->null_lowervp;
299 }
300 #endif
301