1 /*
2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
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 * @(#)nfs_node.c 8.6 (Berkeley) 5/22/95
37 */
38
39 #include <sys/cdefs.h>
40 __FBSDID("$FreeBSD: releng/5.1/sys/nfsclient/nfs_node.c 111119 2003-02-19 05:47:46Z imp $");
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/fnv_hash.h>
45 #include <sys/lock.h>
46 #include <sys/malloc.h>
47 #include <sys/mount.h>
48 #include <sys/namei.h>
49 #include <sys/proc.h>
50 #include <sys/socket.h>
51 #include <sys/sysctl.h>
52 #include <sys/vnode.h>
53
54 #include <vm/uma.h>
55
56 #include <nfs/rpcv2.h>
57 #include <nfs/nfsproto.h>
58 #include <nfsclient/nfs.h>
59 #include <nfsclient/nfsnode.h>
60 #include <nfsclient/nfsmount.h>
61
62 static uma_zone_t nfsnode_zone;
63 static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
64 static u_long nfsnodehash;
65 static int nfs_node_hash_lock;
66
67 #define NFSNOHASH(fhsum) \
68 (&nfsnodehashtbl[(fhsum) & nfsnodehash])
69
70 #define TRUE 1
71 #define FALSE 0
72
73 SYSCTL_DECL(_debug_hashstat);
74
75 /*
76 * Grab an atomic snapshot of the nfsnode hash chain lengths
77 */
78 static int
79 sysctl_debug_hashstat_rawnfsnode(SYSCTL_HANDLER_ARGS)
80 {
81 int error;
82 struct nfsnodehashhead *nnpp;
83 struct nfsnode *nnp;
84 int n_nfsnode;
85 int count;
86
87 n_nfsnode = nfsnodehash + 1; /* nfsnodehash = max index, not count */
88 if (!req->oldptr)
89 return SYSCTL_OUT(req, 0, n_nfsnode * sizeof(int));
90
91 /* Scan hash tables for applicable entries */
92 for (nnpp = nfsnodehashtbl; n_nfsnode > 0; n_nfsnode--, nnpp++) {
93 count = 0;
94 LIST_FOREACH(nnp, nnpp, n_hash) {
95 count++;
96 }
97 error = SYSCTL_OUT(req, (caddr_t)&count, sizeof(count));
98 if (error)
99 return (error);
100 }
101 return (0);
102 }
103 SYSCTL_PROC(_debug_hashstat, OID_AUTO, rawnfsnode, CTLTYPE_INT|CTLFLAG_RD, 0, 0,
104 sysctl_debug_hashstat_rawnfsnode, "S,int", "nfsnode chain lengths");
105
106 static int
107 sysctl_debug_hashstat_nfsnode(SYSCTL_HANDLER_ARGS)
108 {
109 int error;
110 struct nfsnodehashhead *nnpp;
111 struct nfsnode *nnp;
112 int n_nfsnode;
113 int count, maxlength, used, pct;
114
115 if (!req->oldptr)
116 return SYSCTL_OUT(req, 0, 4 * sizeof(int));
117
118 n_nfsnode = nfsnodehash + 1; /* nfsnodehash = max index, not count */
119 used = 0;
120 maxlength = 0;
121
122 /* Scan hash tables for applicable entries */
123 for (nnpp = nfsnodehashtbl; n_nfsnode > 0; n_nfsnode--, nnpp++) {
124 count = 0;
125 LIST_FOREACH(nnp, nnpp, n_hash) {
126 count++;
127 }
128 if (count)
129 used++;
130 if (maxlength < count)
131 maxlength = count;
132 }
133 n_nfsnode = nfsnodehash + 1;
134 pct = (used * 100 * 100) / n_nfsnode;
135 error = SYSCTL_OUT(req, (caddr_t)&n_nfsnode, sizeof(n_nfsnode));
136 if (error)
137 return (error);
138 error = SYSCTL_OUT(req, (caddr_t)&used, sizeof(used));
139 if (error)
140 return (error);
141 error = SYSCTL_OUT(req, (caddr_t)&maxlength, sizeof(maxlength));
142 if (error)
143 return (error);
144 error = SYSCTL_OUT(req, (caddr_t)&pct, sizeof(pct));
145 if (error)
146 return (error);
147 return (0);
148 }
149 SYSCTL_PROC(_debug_hashstat, OID_AUTO, nfsnode, CTLTYPE_INT|CTLFLAG_RD,
150 0, 0, sysctl_debug_hashstat_nfsnode, "I", "nfsnode chain lengths");
151
152 /*
153 * Initialize hash links for nfsnodes
154 * and build nfsnode free list.
155 */
156 void
157 nfs_nhinit(void)
158 {
159
160 nfsnode_zone = uma_zcreate("NFSNODE", sizeof(struct nfsnode), NULL,
161 NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
162 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSHASH, &nfsnodehash);
163 }
164
165 /*
166 * Look up a vnode/nfsnode by file handle.
167 * Callers must check for mount points!!
168 * In all cases, a pointer to a
169 * nfsnode structure is returned.
170 */
171 int
172 nfs_nget(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp)
173 {
174 struct thread *td = curthread; /* XXX */
175 struct nfsnode *np, *np2;
176 struct nfsnodehashhead *nhpp;
177 struct vnode *vp;
178 struct vnode *nvp;
179 int error;
180 int rsflags;
181 struct nfsmount *nmp;
182
183 /*
184 * Calculate nfs mount point and figure out whether the rslock should
185 * be interruptable or not.
186 */
187 nmp = VFSTONFS(mntp);
188 if (nmp->nm_flag & NFSMNT_INT)
189 rsflags = PCATCH;
190 else
191 rsflags = 0;
192
193 retry:
194 nhpp = NFSNOHASH(fnv_32_buf(fhp->fh_bytes, fhsize, FNV1_32_INIT));
195 loop:
196 LIST_FOREACH(np, nhpp, n_hash) {
197 if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
198 bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize))
199 continue;
200 vp = NFSTOV(np);
201 /*
202 * np or vp may become invalid if vget() blocks, so loop
203 */
204 if (vget(vp, LK_EXCLUSIVE|LK_SLEEPFAIL, td))
205 goto loop;
206 *npp = np;
207 return(0);
208 }
209 /*
210 * Obtain a lock to prevent a race condition if the getnewvnode()
211 * or MALLOC() below happens to block.
212 */
213 if (nfs_node_hash_lock) {
214 while (nfs_node_hash_lock) {
215 nfs_node_hash_lock = -1;
216 tsleep(&nfs_node_hash_lock, PVM, "nfsngt", 0);
217 }
218 goto loop;
219 }
220 nfs_node_hash_lock = 1;
221
222 /*
223 * Allocate before getnewvnode since doing so afterward
224 * might cause a bogus v_data pointer to get dereferenced
225 * elsewhere if zalloc should block.
226 */
227 np = uma_zalloc(nfsnode_zone, M_WAITOK);
228
229 error = getnewvnode("nfs", mntp, nfsv2_vnodeop_p, &nvp);
230 if (error) {
231 if (nfs_node_hash_lock < 0)
232 wakeup(&nfs_node_hash_lock);
233 nfs_node_hash_lock = 0;
234 *npp = 0;
235 uma_zfree(nfsnode_zone, np);
236 return (error);
237 }
238 vp = nvp;
239 bzero((caddr_t)np, sizeof *np);
240 vp->v_data = np;
241 np->n_vnode = vp;
242 /*
243 * Insert the nfsnode in the hash queue for its new file handle
244 */
245 LIST_FOREACH(np2, nhpp, n_hash) {
246 if (mntp != NFSTOV(np2)->v_mount || np2->n_fhsize != fhsize ||
247 bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize))
248 continue;
249 vrele(vp);
250 if (nfs_node_hash_lock < 0)
251 wakeup(&nfs_node_hash_lock);
252 nfs_node_hash_lock = 0;
253 uma_zfree(nfsnode_zone, np);
254 goto retry;
255 }
256 LIST_INSERT_HEAD(nhpp, np, n_hash);
257 if (fhsize > NFS_SMALLFH) {
258 MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK);
259 } else
260 np->n_fhp = &np->n_fh;
261 bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
262 np->n_fhsize = fhsize;
263 lockinit(&np->n_rslock, PVFS | rsflags, "nfrslk", 0, LK_NOPAUSE);
264 *npp = np;
265
266 if (nfs_node_hash_lock < 0)
267 wakeup(&nfs_node_hash_lock);
268 nfs_node_hash_lock = 0;
269
270 /*
271 * Lock the new nfsnode.
272 */
273 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
274
275 return (0);
276 }
277
278 int
279 nfs_inactive(struct vop_inactive_args *ap)
280 {
281 struct nfsnode *np;
282 struct sillyrename *sp;
283 struct thread *td = curthread; /* XXX */
284
285 np = VTONFS(ap->a_vp);
286 if (prtactive && vrefcnt(ap->a_vp) != 0)
287 vprint("nfs_inactive: pushing active", ap->a_vp);
288 if (ap->a_vp->v_type != VDIR) {
289 sp = np->n_sillyrename;
290 np->n_sillyrename = NULL;
291 } else
292 sp = NULL;
293 if (sp) {
294 /*
295 * We need a reference to keep the vnode from being
296 * recycled by getnewvnode while we do the I/O
297 * associated with discarding the buffers unless we
298 * are being forcibly unmounted in which case we already
299 * have our own reference.
300 */
301 if (vrefcnt(ap->a_vp) > 0)
302 (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
303 else if (vget(ap->a_vp, 0, td))
304 panic("nfs_inactive: lost vnode");
305 else {
306 (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
307 vrele(ap->a_vp);
308 }
309 /*
310 * Remove the silly file that was rename'd earlier
311 */
312 nfs_removeit(sp);
313 crfree(sp->s_cred);
314 vrele(sp->s_dvp);
315 FREE((caddr_t)sp, M_NFSREQ);
316 }
317 np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT);
318 VOP_UNLOCK(ap->a_vp, 0, ap->a_td);
319 return (0);
320 }
321
322 /*
323 * Reclaim an nfsnode so that it can be used for other purposes.
324 */
325 int
326 nfs_reclaim(struct vop_reclaim_args *ap)
327 {
328 struct vnode *vp = ap->a_vp;
329 struct nfsnode *np = VTONFS(vp);
330 struct nfsdmap *dp, *dp2;
331
332 if (prtactive && vrefcnt(vp) != 0)
333 vprint("nfs_reclaim: pushing active", vp);
334
335 if (np->n_hash.le_prev != NULL) /* XXX beware */
336 LIST_REMOVE(np, n_hash);
337
338 /*
339 * Free up any directory cookie structures and
340 * large file handle structures that might be associated with
341 * this nfs node.
342 */
343 if (vp->v_type == VDIR) {
344 dp = LIST_FIRST(&np->n_cookies);
345 while (dp) {
346 dp2 = dp;
347 dp = LIST_NEXT(dp, ndm_list);
348 FREE((caddr_t)dp2, M_NFSDIROFF);
349 }
350 }
351 if (np->n_fhsize > NFS_SMALLFH) {
352 FREE((caddr_t)np->n_fhp, M_NFSBIGFH);
353 }
354
355 lockdestroy(&np->n_rslock);
356
357 cache_purge(vp);
358 uma_zfree(nfsnode_zone, vp->v_data);
359 vp->v_data = NULL;
360 return (0);
361 }
Cache object: 42226c31f414648a16bac1b5a5f958f5
|