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 * %sccs.include.redist.c%
9 *
10 * @(#)lofs_subr.c 8.5 (Berkeley) 01/21/94
11 *
12 * $Id: lofs_subr.c,v 1.11 1992/05/30 10:05:43 jsp Exp jsp $
13 */
14
15 #include <sys/param.h>
16 #include <sys/systm.h>
17 #include <sys/time.h>
18 #include <sys/types.h>
19 #include <sys/vnode.h>
20 #include <sys/mount.h>
21 #include <sys/namei.h>
22 #include <sys/malloc.h>
23 #include <miscfs/lofs/lofs.h>
24
25 #define LOG2_SIZEVNODE 7 /* log2(sizeof struct vnode) */
26 #define NLOFSCACHE 16
27 #define LOFS_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NLOFSCACHE-1))
28
29 /*
30 * Loopback cache:
31 * Each cache entry holds a reference to the target vnode
32 * along with a pointer to the alias vnode. When an
33 * entry is added the target vnode is VREF'd. When the
34 * alias is removed the target vnode is vrele'd.
35 */
36
37 /*
38 * Cache head
39 */
40 struct lofscache {
41 struct lofsnode *ac_forw;
42 struct lofsnode *ac_back;
43 };
44
45 static struct lofscache lofscache[NLOFSCACHE];
46
47 /*
48 * Initialise cache headers
49 */
50 int
lofs_init()51 lofs_init()
52 {
53 struct lofscache *ac;
54
55 for (ac = lofscache; ac < lofscache + NLOFSCACHE; ac++)
56 ac->ac_forw = ac->ac_back = (struct lofsnode *) ac;
57
58 return (0);
59 }
60
61 /*
62 * Compute hash list for given target vnode
63 */
64 static struct lofscache *
lofs_hash(targetvp)65 lofs_hash(targetvp)
66 struct vnode *targetvp;
67 {
68
69 return (&lofscache[LOFS_NHASH(targetvp)]);
70 }
71
72 /*
73 * Make a new lofsnode node.
74 * Vp is the alias vnode, lofsvp is the target vnode.
75 * Maintain a reference to (targetvp).
76 */
77 static void
lofs_alloc(vp,targetvp)78 lofs_alloc(vp, targetvp)
79 struct vnode *vp;
80 struct vnode *targetvp;
81 {
82 struct lofscache *hd;
83 struct lofsnode *a;
84
85 MALLOC(a, struct lofsnode *, sizeof(struct lofsnode), M_TEMP, M_WAITOK);
86 a->a_vnode = vp;
87 vp->v_data = a;
88 VREF(targetvp);
89 a->a_lofsvp = targetvp;
90 hd = lofs_hash(targetvp);
91 insque(a, hd);
92
93 }
94
95
96 /*
97 * Return alias for target vnode if already exists, else 0.
98 */
99 static struct lofsnode *
lofs_find(mp,targetvp)100 lofs_find(mp, targetvp)
101 struct mount *mp;
102 struct vnode *targetvp;
103 {
104 struct lofscache *hd;
105 struct lofsnode *a;
106
107 /*
108 * Find hash base, and then search the (two-way) linked
109 * list looking for a lofsnode structure which is referencing
110 * the target vnode. If found, the increment the lofsnode
111 * reference count (but NOT the target vnode's VREF counter).
112 */
113 hd = lofs_hash(targetvp);
114
115 for (a = hd->ac_forw; a != (struct lofsnode *) hd; a = a->a_forw) {
116 if (a->a_lofsvp == targetvp && a->a_vnode->v_mount == mp) {
117 return (a);
118 }
119 }
120
121 return (0);
122 }
123
124 static int
lofs_alias(mp,targetvp,newvpp)125 lofs_alias(mp, targetvp, newvpp)
126 struct mount *mp;
127 struct vnode *targetvp;
128 struct vnode **newvpp;
129 {
130 struct lofsnode *ap;
131 struct vnode *aliasvp;
132
133 if (targetvp->v_type != VDIR || targetvp->v_op == lofs_vnodeop_p) {
134 *newvpp = targetvp;
135 return (0);
136 }
137
138 ap = lofs_find(mp, targetvp);
139
140 if (ap) {
141 /*
142 * Take another reference to the alias vnode
143 */
144 aliasvp = ap->a_vnode;
145 VREF(aliasvp);
146 } else {
147 int error;
148
149 /*
150 * Get new vnode.
151 */
152 if (error = getnewvnode(VT_LOFS, mp, lofs_vnodeop_p, &aliasvp))
153 return (error);
154
155 /*
156 * Must be a directory
157 */
158 aliasvp->v_type = VDIR;
159
160 /*
161 * Make new vnode reference the lofsnode.
162 */
163 lofs_alloc(aliasvp, targetvp);
164
165 /*
166 * aliasvp is already VREF'd by getnewvnode()
167 */
168 }
169
170 vrele(targetvp);
171
172 *newvpp = aliasvp;
173 return (0);
174 }
175
176 /*
177 * Try to find an existing lofsnode vnode refering
178 * to it, otherwise make a new lofsnode vnode which
179 * contains a reference to the target vnode.
180 */
181 int
make_lofs(mp,vpp)182 make_lofs(mp, vpp)
183 struct mount *mp;
184 struct vnode **vpp;
185 {
186 struct vnode *targetvp;
187
188 /*
189 * (targetvp) is locked at this point.
190 */
191 targetvp = *vpp;
192
193 /*
194 * Try to find an existing reference to the target vnodes.
195 */
196 return (lofs_alias(mp, targetvp, vpp));
197 }
198
199