]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/nfsclient/nfs_node.c
This commit was generated by cvs2svn to compensate for changes in r92442,
[FreeBSD/FreeBSD.git] / sys / nfsclient / nfs_node.c
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$");
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/vm_zone.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 vm_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 TRUE    1
68 #define FALSE   0
69
70 SYSCTL_DECL(_debug_hashstat);
71
72 /*
73  * Grab an atomic snapshot of the nfsnode hash chain lengths
74  */
75 static int
76 sysctl_debug_hashstat_rawnfsnode(SYSCTL_HANDLER_ARGS)
77 {
78         int error;
79         struct nfsnodehashhead *nnpp;
80         struct nfsnode *nnp;
81         int n_nfsnode;
82         int count;
83
84         n_nfsnode = nfsnodehash + 1;    /* nfsnodehash = max index, not count */
85         if (!req->oldptr)
86                 return SYSCTL_OUT(req, 0, n_nfsnode * sizeof(int));
87
88         /* Scan hash tables for applicable entries */
89         for (nnpp = nfsnodehashtbl; n_nfsnode > 0; n_nfsnode--, nnpp++) {
90                 count = 0;
91                 LIST_FOREACH(nnp, nnpp, n_hash) {
92                         count++;
93                 }
94                 error = SYSCTL_OUT(req, (caddr_t)&count, sizeof(count));
95                 if (error)
96                         return (error);
97         }
98         return (0);
99 }
100 SYSCTL_PROC(_debug_hashstat, OID_AUTO, rawnfsnode, CTLTYPE_INT|CTLFLAG_RD, 0, 0,
101             sysctl_debug_hashstat_rawnfsnode, "S,int", "nfsnode chain lengths");
102
103 static int
104 sysctl_debug_hashstat_nfsnode(SYSCTL_HANDLER_ARGS)
105 {
106         int error;
107         struct nfsnodehashhead *nnpp;
108         struct nfsnode *nnp;
109         int n_nfsnode;
110         int count, maxlength, used, pct;
111
112         if (!req->oldptr)
113                 return SYSCTL_OUT(req, 0, 4 * sizeof(int));
114
115         n_nfsnode = nfsnodehash + 1;    /* nfsnodehash = max index, not count */
116         used = 0;
117         maxlength = 0;
118
119         /* Scan hash tables for applicable entries */
120         for (nnpp = nfsnodehashtbl; n_nfsnode > 0; n_nfsnode--, nnpp++) {
121                 count = 0;
122                 LIST_FOREACH(nnp, nnpp, n_hash) {
123                         count++;
124                 }
125                 if (count)
126                         used++;
127                 if (maxlength < count)
128                         maxlength = count;
129         }
130         n_nfsnode = nfsnodehash + 1;
131         pct = (used * 100 * 100) / n_nfsnode;
132         error = SYSCTL_OUT(req, (caddr_t)&n_nfsnode, sizeof(n_nfsnode));
133         if (error)
134                 return (error);
135         error = SYSCTL_OUT(req, (caddr_t)&used, sizeof(used));
136         if (error)
137                 return (error);
138         error = SYSCTL_OUT(req, (caddr_t)&maxlength, sizeof(maxlength));
139         if (error)
140                 return (error);
141         error = SYSCTL_OUT(req, (caddr_t)&pct, sizeof(pct));
142         if (error)
143                 return (error);
144         return (0);
145 }
146 SYSCTL_PROC(_debug_hashstat, OID_AUTO, nfsnode, CTLTYPE_INT|CTLFLAG_RD,
147         0, 0, sysctl_debug_hashstat_nfsnode, "I", "nfsnode chain lengths");
148
149 /*
150  * Initialize hash links for nfsnodes
151  * and build nfsnode free list.
152  */
153 void
154 nfs_nhinit(void)
155 {
156
157         nfsnode_zone = zinit("NFSNODE", sizeof(struct nfsnode), 0, 0, 1);
158         nfsnodehashtbl = hashinit(desiredvnodes, M_NFSHASH, &nfsnodehash);
159 }
160
161 /*
162  * Look up a vnode/nfsnode by file handle.
163  * Callers must check for mount points!!
164  * In all cases, a pointer to a
165  * nfsnode structure is returned.
166  */
167 int
168 nfs_nget(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp)
169 {
170         struct thread *td = curthread;  /* XXX */
171         struct nfsnode *np, *np2;
172         struct nfsnodehashhead *nhpp;
173         struct vnode *vp;
174         struct vnode *nvp;
175         int error;
176         int rsflags;
177         struct nfsmount *nmp;
178
179         /*
180          * Calculate nfs mount point and figure out whether the rslock should
181          * be interruptable or not.
182          */
183         nmp = VFSTONFS(mntp);
184         if (nmp->nm_flag & NFSMNT_INT)
185                 rsflags = PCATCH;
186         else
187                 rsflags = 0;
188
189 retry:
190         nhpp = NFSNOHASH(fnv_32_buf(fhp->fh_bytes, fhsize, FNV1_32_INIT));
191 loop:
192         LIST_FOREACH(np, nhpp, n_hash) {
193                 if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
194                     bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize))
195                         continue;
196                 vp = NFSTOV(np);
197                 /*
198                  * np or vp may become invalid if vget() blocks, so loop 
199                  */
200                 if (vget(vp, LK_EXCLUSIVE|LK_SLEEPFAIL, td))
201                         goto loop;
202                 *npp = np;
203                 return(0);
204         }
205         /*
206          * Obtain a lock to prevent a race condition if the getnewvnode()
207          * or MALLOC() below happens to block.
208          */
209         if (nfs_node_hash_lock) {
210                 while (nfs_node_hash_lock) {
211                         nfs_node_hash_lock = -1;
212                         tsleep(&nfs_node_hash_lock, PVM, "nfsngt", 0);
213                 }
214                 goto loop;
215         }
216         nfs_node_hash_lock = 1;
217
218         /*
219          * Allocate before getnewvnode since doing so afterward
220          * might cause a bogus v_data pointer to get dereferenced
221          * elsewhere if zalloc should block.
222          */
223         np = zalloc(nfsnode_zone);
224
225         error = getnewvnode(VT_NFS, mntp, nfsv2_vnodeop_p, &nvp);
226         if (error) {
227                 if (nfs_node_hash_lock < 0)
228                         wakeup(&nfs_node_hash_lock);
229                 nfs_node_hash_lock = 0;
230                 *npp = 0;
231                 zfree(nfsnode_zone, np);
232                 return (error);
233         }
234         vp = nvp;
235         bzero((caddr_t)np, sizeof *np);
236         vp->v_data = np;
237         np->n_vnode = vp;
238         /*
239          * Insert the nfsnode in the hash queue for its new file handle
240          */
241         LIST_FOREACH(np2, nhpp, n_hash) {
242                 if (mntp != NFSTOV(np2)->v_mount || np2->n_fhsize != fhsize ||
243                     bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize))
244                         continue;
245                 vrele(vp);
246                 if (nfs_node_hash_lock < 0)
247                         wakeup(&nfs_node_hash_lock);
248                 nfs_node_hash_lock = 0;
249                 zfree(nfsnode_zone, np);
250                 goto retry;
251         }
252         LIST_INSERT_HEAD(nhpp, np, n_hash);
253         if (fhsize > NFS_SMALLFH) {
254                 MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK);
255         } else
256                 np->n_fhp = &np->n_fh;
257         bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
258         np->n_fhsize = fhsize;
259         lockinit(&np->n_rslock, PVFS | rsflags, "nfrslk", 0, LK_NOPAUSE);
260         lockinit(&vp->v_lock, PVFS, "nfsnlk", 0, LK_NOPAUSE);
261         *npp = np;
262
263         if (nfs_node_hash_lock < 0)
264                 wakeup(&nfs_node_hash_lock);
265         nfs_node_hash_lock = 0;
266
267         /*
268          * Lock the new nfsnode.
269          */
270         vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, td);
271
272         return (0);
273 }
274
275 int
276 nfs_inactive(struct vop_inactive_args *ap)
277 {
278         struct nfsnode *np;
279         struct sillyrename *sp;
280         struct thread *td = curthread;  /* XXX */
281
282         np = VTONFS(ap->a_vp);
283         if (prtactive && ap->a_vp->v_usecount != 0)
284                 vprint("nfs_inactive: pushing active", ap->a_vp);
285         if (ap->a_vp->v_type != VDIR) {
286                 sp = np->n_sillyrename;
287                 np->n_sillyrename = (struct sillyrename *)0;
288         } else
289                 sp = (struct sillyrename *)0;
290         if (sp) {
291                 /*
292                  * We need a reference to keep the vnode from being
293                  * recycled by getnewvnode while we do the I/O
294                  * associated with discarding the buffers unless we
295                  * are being forcibly unmounted in which case we already
296                  * have our own reference.
297                  */
298                 if (ap->a_vp->v_usecount > 0)
299                         (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
300                 else if (vget(ap->a_vp, 0, td))
301                         panic("nfs_inactive: lost vnode");
302                 else {
303                         (void) nfs_vinvalbuf(ap->a_vp, 0, sp->s_cred, td, 1);
304                         vrele(ap->a_vp);
305                 }
306                 /*
307                  * Remove the silly file that was rename'd earlier
308                  */
309                 nfs_removeit(sp);
310                 crfree(sp->s_cred);
311                 vrele(sp->s_dvp);
312                 FREE((caddr_t)sp, M_NFSREQ);
313         }
314         np->n_flag &= (NMODIFIED | NFLUSHINPROG | NFLUSHWANT);
315         VOP_UNLOCK(ap->a_vp, 0, ap->a_td);
316         return (0);
317 }
318
319 /*
320  * Reclaim an nfsnode so that it can be used for other purposes.
321  */
322 int
323 nfs_reclaim(struct vop_reclaim_args *ap)
324 {
325         struct vnode *vp = ap->a_vp;
326         struct nfsnode *np = VTONFS(vp);
327         struct nfsdmap *dp, *dp2;
328
329         if (prtactive && vp->v_usecount != 0)
330                 vprint("nfs_reclaim: pushing active", vp);
331
332         if (np->n_hash.le_prev != NULL)         /* XXX beware */
333                 LIST_REMOVE(np, n_hash);
334
335         /*
336          * Free up any directory cookie structures and
337          * large file handle structures that might be associated with
338          * this nfs node.
339          */
340         if (vp->v_type == VDIR) {
341                 dp = LIST_FIRST(&np->n_cookies);
342                 while (dp) {
343                         dp2 = dp;
344                         dp = LIST_NEXT(dp, ndm_list);
345                         FREE((caddr_t)dp2, M_NFSDIROFF);
346                 }
347         }
348         if (np->n_fhsize > NFS_SMALLFH) {
349                 FREE((caddr_t)np->n_fhp, M_NFSBIGFH);
350         }
351
352         lockdestroy(&np->n_rslock);
353
354         cache_purge(vp);
355         zfree(nfsnode_zone, vp->v_data);
356         vp->v_data = (void *)0;
357         return (0);
358 }
359
360 #if 0
361 /*
362  * Lock an nfsnode
363  */
364 int
365 nfs_lock(struct vop_lock_args *ap)
366 {
367         struct vnode *vp = ap->a_vp;
368
369         /*
370          * Ugh, another place where interruptible mounts will get hung.
371          * If you make this sleep interruptible, then you have to fix all
372          * the VOP_LOCK() calls to expect interruptibility.
373          */
374         while (vp->v_flag & VXLOCK) {
375                 vp->v_flag |= VXWANT;
376                 (void) tsleep((caddr_t)vp, PINOD, "nfslck", 0);
377         }
378         if (vp->v_tag == VT_NON)
379                 return (ENOENT);
380
381 #if 0
382         /*
383          * Only lock regular files.  If a server crashed while we were
384          * holding a directory lock, we could easily end up sleeping
385          * until the server rebooted while holding a lock on the root.
386          * Locks are only needed for protecting critical sections in
387          * VMIO at the moment.
388          * New vnodes will have type VNON but they should be locked
389          * since they may become VREG.  This is checked in loadattrcache
390          * and unwanted locks are released there.
391          */
392         if (vp->v_type == VREG || vp->v_type == VNON) {
393                 while (np->n_flag & NLOCKED) {
394                         np->n_flag |= NWANTED;
395                         (void) tsleep((caddr_t) np, PINOD, "nfslck2", 0);
396                         /*
397                          * If the vnode has transmuted into a VDIR while we
398                          * were asleep, then skip the lock.
399                          */
400                         if (vp->v_type != VREG && vp->v_type != VNON)
401                                 return (0);
402                 }
403                 np->n_flag |= NLOCKED;
404         }
405 #endif
406
407         return (0);
408 }
409
410 /*
411  * Unlock an nfsnode
412  */
413 int
414 nfs_unlock(struct vop_unlock_args *ap)
415 {
416 #if 0
417         struct vnode* vp = ap->a_vp;
418         struct nfsnode* np = VTONFS(vp);
419
420         if (vp->v_type == VREG || vp->v_type == VNON) {
421                 if (!(np->n_flag & NLOCKED))
422                         panic("nfs_unlock: nfsnode not locked");
423                 np->n_flag &= ~NLOCKED;
424                 if (np->n_flag & NWANTED) {
425                         np->n_flag &= ~NWANTED;
426                         wakeup((caddr_t) np);
427                 }
428         }
429 #endif
430
431         return (0);
432 }
433
434 /*
435  * Check for a locked nfsnode
436  */
437 int
438 nfs_islocked(struct vop_islocked_args *ap)
439 {
440
441         return VTONFS(ap->a_vp)->n_flag & NLOCKED ? 1 : 0;
442 }
443 #endif
444