Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / vfs / nullfs / null_subr.c
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: src/sys/miscfs/nullfs/null_subr.c,v 1.21.2.4 2001/06/26 04:20:09 bp Exp $
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
64 static LIST_HEAD(null_node_hashhead, null_node) *null_node_hashtbl;
65 static u_long null_node_hash;
66 struct lock null_hashlock;
67
68 static MALLOC_DEFINE(M_NULLFSHASH, "NULLFS hash", "NULLFS hash table");
69 MALLOC_DEFINE(M_NULLFSNODE, "NULLFS node", "NULLFS vnode private part");
70
71 static int      null_node_alloc(struct mount *mp, struct vnode *lowervp,
72                                      struct vnode **vpp);
73 static struct vnode *
74                 null_node_find(struct mount *mp, struct vnode *lowervp);
75
76 /*
77  * Initialise cache headers
78  */
79 int
80 nullfs_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
90 int
91 nullfs_uninit(vfsp)
92         struct vfsconf *vfsp;
93 {
94
95         if (null_node_hashtbl) {
96                 free(null_node_hashtbl, M_NULLFSHASH);
97         }
98         return (0);
99 }
100
101 /*
102  * Return a VREF'ed alias for lower vnode if already exists, else 0.
103  * Lower vnode should be locked on entry and will be left locked on exit.
104  */
105 static struct vnode *
106 null_node_find(mp, lowervp)
107         struct mount *mp;
108         struct vnode *lowervp;
109 {
110         struct proc *p = curproc;       /* XXX */
111         struct null_node_hashhead *hd;
112         struct null_node *a;
113         struct vnode *vp;
114
115         /*
116          * Find hash base, and then search the (two-way) linked
117          * list looking for a null_node structure which is referencing
118          * the lower vnode.  If found, the increment the null_node
119          * reference count (but NOT the lower vnode's VREF counter).
120          */
121         hd = NULL_NHASH(lowervp);
122 loop:
123         lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p);
124         LIST_FOREACH(a, hd, null_hash) {
125                 if (a->null_lowervp == lowervp && NULLTOV(a)->v_mount == mp) {
126                         vp = NULLTOV(a);
127                         lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
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(vp, LK_EXCLUSIVE | LK_CANRECURSE, p)) {
134                                 printf ("null_node_find: vget failed.\n");
135                                 goto loop;
136                         }
137                         VOP_UNLOCK(lowervp, 0, p);
138                         return (vp);
139                 }
140         }
141         lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
142
143         return NULLVP;
144 }
145
146
147 /*
148  * Make a new null_node node.
149  * Vp is the alias vnode, lofsvp is the lower vnode.
150  * Maintain a reference to (lowervp).
151  */
152 static int
153 null_node_alloc(mp, lowervp, vpp)
154         struct mount *mp;
155         struct vnode *lowervp;
156         struct vnode **vpp;
157 {
158         struct proc *p = curproc;       /* XXX */
159         struct null_node_hashhead *hd;
160         struct null_node *xp;
161         struct vnode *othervp, *vp;
162         int error;
163
164         /*
165          * Do the MALLOC before the getnewvnode since doing so afterward
166          * might cause a bogus v_data pointer to get dereferenced
167          * elsewhere if MALLOC should block.
168          */
169         MALLOC(xp, struct null_node *, sizeof(struct null_node),
170             M_NULLFSNODE, M_WAITOK);
171
172         error = getnewvnode(VT_NULL, mp, null_vnodeop_p, vpp);
173         if (error) {
174                 FREE(xp, M_NULLFSNODE);
175                 return (error);
176         }
177         vp = *vpp;
178
179         vp->v_type = lowervp->v_type;
180         lockinit(&xp->null_lock, PINOD, "nullnode", 0, LK_CANRECURSE);
181         xp->null_vnode = vp;
182         vp->v_data = xp;
183         xp->null_lowervp = lowervp;
184         /*
185          * Before we insert our new node onto the hash chains,
186          * check to see if someone else has beaten us to it.
187          * (We could have slept in MALLOC.)
188          */
189         othervp = null_node_find(mp, lowervp);
190         if (othervp) {
191                 vp->v_data = NULL;
192                 FREE(xp, M_NULLFSNODE);
193                 vp->v_type = VBAD;      /* node is discarded */
194                 vrele(vp);
195                 *vpp = othervp;
196                 return 0;
197         }
198
199         /*
200          * From NetBSD:
201          * Now lock the new node. We rely on the fact that we were passed
202          * a locked vnode. If the lower node is exporting a struct lock
203          * (v_vnlock != NULL) then we just set the upper v_vnlock to the
204          * lower one, and both are now locked. If the lower node is exporting
205          * NULL, then we copy that up and manually lock the new vnode.
206          */
207
208         lockmgr(&null_hashlock, LK_EXCLUSIVE, NULL, p);
209         vp->v_vnlock = lowervp->v_vnlock;
210         error = VOP_LOCK(vp, LK_EXCLUSIVE | LK_THISLAYER, p);
211         if (error)
212                 panic("null_node_alloc: can't lock new vnode\n");
213
214         VREF(lowervp);
215         hd = NULL_NHASH(lowervp);
216         LIST_INSERT_HEAD(hd, xp, null_hash);
217         lockmgr(&null_hashlock, LK_RELEASE, NULL, p);
218         return 0;
219 }
220
221
222 /*
223  * Try to find an existing null_node vnode refering to the given underlying
224  * vnode (which should be locked). If no vnode found, create a new null_node
225  * vnode which contains a reference to the lower vnode.
226  */
227 int
228 null_node_create(mp, lowervp, newvpp)
229         struct mount *mp;
230         struct vnode *lowervp;
231         struct vnode **newvpp;
232 {
233         struct vnode *aliasvp;
234
235         aliasvp = null_node_find(mp, lowervp);
236         if (aliasvp) {
237                 /*
238                  * null_node_find has taken another reference
239                  * to the alias vnode.
240                  */
241                 vrele(lowervp);
242 #ifdef NULLFS_DEBUG
243                 vprint("null_node_create: exists", aliasvp);
244 #endif
245         } else {
246                 int error;
247
248                 /*
249                  * Get new vnode.
250                  */
251                 NULLFSDEBUG("null_node_create: create new alias vnode\n");
252
253                 /*
254                  * Make new vnode reference the null_node.
255                  */
256                 error = null_node_alloc(mp, lowervp, &aliasvp);
257                 if (error)
258                         return error;
259
260                 /*
261                  * aliasvp is already VREF'd by getnewvnode()
262                  */
263         }
264
265 #ifdef DIAGNOSTIC
266         if (lowervp->v_usecount < 1) {
267                 /* Should never happen... */
268                 vprint ("null_node_create: alias ", aliasvp);
269                 vprint ("null_node_create: lower ", lowervp);
270                 panic ("null_node_create: lower has 0 usecount.");
271         };
272 #endif
273
274 #ifdef NULLFS_DEBUG
275         vprint("null_node_create: alias", aliasvp);
276         vprint("null_node_create: lower", lowervp);
277 #endif
278
279         *newvpp = aliasvp;
280         return (0);
281 }
282
283 #ifdef DIAGNOSTIC
284 #include "opt_ddb.h"
285
286 #ifdef DDB
287 #define null_checkvp_barrier    1
288 #else
289 #define null_checkvp_barrier    0
290 #endif
291
292 struct vnode *
293 null_checkvp(vp, fil, lno)
294         struct vnode *vp;
295         char *fil;
296         int lno;
297 {
298         struct null_node *a = VTONULL(vp);
299 #ifdef notyet
300         /*
301          * Can't do this check because vop_reclaim runs
302          * with a funny vop vector.
303          */
304         if (vp->v_op != null_vnodeop_p) {
305                 printf ("null_checkvp: on non-null-node\n");
306                 while (null_checkvp_barrier) /*WAIT*/ ;
307                 panic("null_checkvp");
308         };
309 #endif
310         if (a->null_lowervp == NULLVP) {
311                 /* Should never happen */
312                 int i; u_long *p;
313                 printf("vp = %p, ZERO ptr\n", (void *)vp);
314                 for (p = (u_long *) a, i = 0; i < 8; i++)
315                         printf(" %lx", p[i]);
316                 printf("\n");
317                 /* wait for debugger */
318                 while (null_checkvp_barrier) /*WAIT*/ ;
319                 panic("null_checkvp");
320         }
321         if (a->null_lowervp->v_usecount < 1) {
322                 int i; u_long *p;
323                 printf("vp = %p, unref'ed lowervp\n", (void *)vp);
324                 for (p = (u_long *) a, i = 0; i < 8; i++)
325                         printf(" %lx", p[i]);
326                 printf("\n");
327                 /* wait for debugger */
328                 while (null_checkvp_barrier) /*WAIT*/ ;
329                 panic ("null with unref'ed lowervp");
330         };
331 #ifdef notyet
332         printf("null %x/%d -> %x/%d [%s, %d]\n",
333                 NULLTOV(a), NULLTOV(a)->v_usecount,
334                 a->null_lowervp, a->null_lowervp->v_usecount,
335                 fil, lno);
336 #endif
337         return a->null_lowervp;
338 }
339 #endif