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