]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/fs/nullfs/null_subr.c
This commit was generated by cvs2svn to compensate for changes in r55289,
[FreeBSD/FreeBSD.git] / sys / fs / 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$
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