3757517df36545c80ca465d2be2f92e8a5ac3e4b
[dragonfly.git] / sys / vfs / nfs / 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  * $FreeBSD: src/sys/nfs/nfs_node.c,v 1.36.2.3 2002/01/05 22:25:04 dillon Exp $
38  * $DragonFly: src/sys/vfs/nfs/nfs_node.c,v 1.27 2007/08/08 00:12:51 swildner Exp $
39  */
40
41
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/proc.h>
45 #include <sys/mount.h>
46 #include <sys/namei.h>
47 #include <sys/vnode.h>
48 #include <sys/malloc.h>
49 #include <sys/kernel.h>
50 #include <sys/fnv_hash.h>
51 #include <sys/objcache.h>
52
53 #include "rpcv2.h"
54 #include "nfsproto.h"
55 #include "nfs.h"
56 #include "nfsmount.h"
57 #include "nfsnode.h"
58
59 static MALLOC_DEFINE(M_NFSNODE, "NFS node", "NFS node");
60
61 static struct objcache *nfsnode_objcache;
62 static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
63 static u_long nfsnodehash;
64 static lwkt_token nfsnhash_token = LWKT_TOKEN_INITIALIZER(nfsnhash_token);
65 static struct lock nfsnhash_lock;
66
67 #define TRUE    1
68 #define FALSE   0
69
70 #define NFSNOHASH(fhsum)        (&nfsnodehashtbl[(fhsum) & nfsnodehash])
71
72 /*
73  * Initialize hash links for nfsnodes
74  * and build nfsnode free list.
75  */
76 void
77 nfs_nhinit(void)
78 {
79         nfsnode_objcache = objcache_create_simple(M_NFSNODE, sizeof(struct nfsnode));
80         nfsnodehashtbl = hashinit(desiredvnodes, M_NFSHASH, &nfsnodehash);
81         lockinit(&nfsnhash_lock, "nfsnht", 0, 0);
82 }
83
84 /*
85  * Look up a vnode/nfsnode by file handle.
86  * Callers must check for mount points!!
87  * In all cases, a pointer to a
88  * nfsnode structure is returned.
89  */
90
91 int
92 nfs_nget(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp)
93 {
94         struct nfsnode *np, *np2;
95         struct nfsnodehashhead *nhpp;
96         struct vnode *vp;
97         int error;
98         int lkflags;
99         struct nfsmount *nmp;
100
101         /*
102          * Calculate nfs mount point and figure out whether the rslock should
103          * be interruptable or not.
104          */
105         nmp = VFSTONFS(mntp);
106         if (nmp->nm_flag & NFSMNT_INT)
107                 lkflags = LK_PCATCH;
108         else
109                 lkflags = 0;
110
111         lwkt_gettoken(&nfsnhash_token);
112
113 retry:
114         nhpp = NFSNOHASH(fnv_32_buf(fhp->fh_bytes, fhsize, FNV1_32_INIT));
115 loop:
116         for (np = nhpp->lh_first; np; np = np->n_hash.le_next) {
117                 if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
118                     bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize)) {
119                         continue;
120                 }
121                 vp = NFSTOV(np);
122                 if (vget(vp, LK_EXCLUSIVE))
123                         goto loop;
124                 for (np = nhpp->lh_first; np; np = np->n_hash.le_next) {
125                         if (mntp == NFSTOV(np)->v_mount &&
126                             np->n_fhsize == fhsize &&
127                             bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize) == 0
128                         ) {
129                                 break;
130                         }
131                 }
132                 if (np == NULL || NFSTOV(np) != vp) {
133                         vput(vp);
134                         goto loop;
135                 }
136                 *npp = np;
137                 lwkt_reltoken(&nfsnhash_token);
138                 return(0);
139         }
140
141         /*
142          * Obtain a lock to prevent a race condition if the getnewvnode()
143          * or MALLOC() below happens to block.
144          */
145         if (lockmgr(&nfsnhash_lock, LK_EXCLUSIVE | LK_SLEEPFAIL))
146                 goto loop;
147
148         /*
149          * Allocate before getnewvnode since doing so afterward
150          * might cause a bogus v_data pointer to get dereferenced
151          * elsewhere if objcache should block.
152          */
153         np = objcache_get(nfsnode_objcache, M_WAITOK);
154                 
155         error = getnewvnode(VT_NFS, mntp, &vp, 0, 0);
156         if (error) {
157                 lockmgr(&nfsnhash_lock, LK_RELEASE);
158                 *npp = NULL;
159                 objcache_put(nfsnode_objcache, np);
160                 lwkt_reltoken(&nfsnhash_token);
161                 return (error);
162         }
163
164         /*
165          * Initialize most of (np).
166          */
167         bzero(np, sizeof (*np));
168         if (fhsize > NFS_SMALLFH) {
169                 MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK);
170         } else {
171                 np->n_fhp = &np->n_fh;
172         }
173         bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
174         np->n_fhsize = fhsize;
175         lockinit(&np->n_rslock, "nfrslk", 0, lkflags);
176
177         /*
178          * Validate that we did not race another nfs_nget() due to blocking
179          * here and there.
180          */
181         for (np2 = nhpp->lh_first; np2 != 0; np2 = np2->n_hash.le_next) {
182                 if (mntp != NFSTOV(np2)->v_mount || np2->n_fhsize != fhsize ||
183                     bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize)) {
184                         continue;
185                 }
186                 vx_put(vp);
187                 lockmgr(&nfsnhash_lock, LK_RELEASE);
188
189                 if (np->n_fhsize > NFS_SMALLFH)
190                         FREE((caddr_t)np->n_fhp, M_NFSBIGFH);
191                 np->n_fhp = NULL;
192                 objcache_put(nfsnode_objcache, np);
193                 goto retry;
194         }
195
196         /*
197          * Finish connecting up (np, vp) and insert the nfsnode in the
198          * hash for its new file handle.
199          *
200          * nvp is locked & refd so effectively so is np.
201          */
202         np->n_vnode = vp;
203         vp->v_data = np;
204         LIST_INSERT_HEAD(nhpp, np, n_hash);
205         *npp = np;
206         lockmgr(&nfsnhash_lock, LK_RELEASE);
207         lwkt_reltoken(&nfsnhash_token);
208
209         return (0);
210 }
211
212 /*
213  * Nonblocking version of nfs_nget()
214  */
215 int
216 nfs_nget_nonblock(struct mount *mntp, nfsfh_t *fhp, int fhsize,
217                   struct nfsnode **npp)
218 {
219         struct nfsnode *np, *np2;
220         struct nfsnodehashhead *nhpp;
221         struct vnode *vp;
222         int error;
223         int lkflags;
224         struct nfsmount *nmp;
225
226         /*
227          * Calculate nfs mount point and figure out whether the rslock should
228          * be interruptable or not.
229          */
230         nmp = VFSTONFS(mntp);
231         if (nmp->nm_flag & NFSMNT_INT)
232                 lkflags = LK_PCATCH;
233         else
234                 lkflags = 0;
235         vp = NULL;
236         *npp = NULL;
237
238         lwkt_gettoken(&nfsnhash_token);
239
240 retry:
241         nhpp = NFSNOHASH(fnv_32_buf(fhp->fh_bytes, fhsize, FNV1_32_INIT));
242 loop:
243         for (np = nhpp->lh_first; np; np = np->n_hash.le_next) {
244                 if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
245                     bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize)) {
246                         continue;
247                 }
248                 if (vp == NULL) {
249                         vp = NFSTOV(np);
250                         if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT)) {
251                                 error = EWOULDBLOCK;
252                                 goto fail;
253                         }
254                         goto loop;
255                 }
256                 if (NFSTOV(np) != vp) {
257                         vput(vp);
258                         goto loop;
259                 }
260                 *npp = np;
261                 lwkt_reltoken(&nfsnhash_token);
262                 return(0);
263         }
264
265         /*
266          * Not found.  If we raced and had acquired a vp we have to release
267          * it here.
268          */
269         if (vp) {
270                 vput(vp);
271                 vp = NULL;
272         }
273
274         /*
275          * Obtain a lock to prevent a race condition if the getnewvnode()
276          * or MALLOC() below happens to block.
277          */
278         if (lockmgr(&nfsnhash_lock, LK_EXCLUSIVE | LK_SLEEPFAIL))
279                 goto loop;
280
281         /*
282          * Entry not found, allocate a new entry.
283          *
284          * Allocate before getnewvnode since doing so afterward
285          * might cause a bogus v_data pointer to get dereferenced
286          * elsewhere if objcache should block.
287          */
288         np = objcache_get(nfsnode_objcache, M_WAITOK);
289
290         error = getnewvnode(VT_NFS, mntp, &vp, 0, 0);
291         if (error) {
292                 lockmgr(&nfsnhash_lock, LK_RELEASE);
293                 objcache_put(nfsnode_objcache, np);
294                 lwkt_reltoken(&nfsnhash_token);
295                 return (error);
296         }
297
298         /*
299          * Initialize most of (np).
300          */
301         bzero(np, sizeof (*np));
302         if (fhsize > NFS_SMALLFH) {
303                 MALLOC(np->n_fhp, nfsfh_t *, fhsize, M_NFSBIGFH, M_WAITOK);
304         } else {
305                 np->n_fhp = &np->n_fh;
306         }
307         bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
308         np->n_fhsize = fhsize;
309         lockinit(&np->n_rslock, "nfrslk", 0, lkflags);
310
311         /*
312          * Validate that we did not race another nfs_nget() due to blocking
313          * here and there.
314          */
315         for (np2 = nhpp->lh_first; np2 != 0; np2 = np2->n_hash.le_next) {
316                 if (mntp != NFSTOV(np2)->v_mount || np2->n_fhsize != fhsize ||
317                     bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize)) {
318                         continue;
319                 }
320                 vx_put(vp);
321                 lockmgr(&nfsnhash_lock, LK_RELEASE);
322
323                 if (np->n_fhsize > NFS_SMALLFH)
324                         FREE((caddr_t)np->n_fhp, M_NFSBIGFH);
325                 np->n_fhp = NULL;
326                 objcache_put(nfsnode_objcache, np);
327
328                 /*
329                  * vp state is retained on retry/loop so we must NULL it
330                  * out here or fireworks may ensue.
331                  */
332                 vp = NULL;
333                 goto retry;
334         }
335
336         /*
337          * Finish connecting up (np, vp) and insert the nfsnode in the
338          * hash for its new file handle.
339          *
340          * nvp is locked & refd so effectively so is np.
341          */
342         np->n_vnode = vp;
343         vp->v_data = np;
344         LIST_INSERT_HEAD(nhpp, np, n_hash);
345
346         /*
347          * nvp is locked & refd so effectively so is np.
348          */
349         *npp = np;
350         error = 0;
351         lockmgr(&nfsnhash_lock, LK_RELEASE);
352 fail:
353         lwkt_reltoken(&nfsnhash_token);
354         return (error);
355 }
356
357 /*
358  * nfs_inactive(struct vnode *a_vp)
359  *
360  * NOTE: the passed vnode is locked but not referenced.  On return the
361  * vnode must be unlocked and not referenced.
362  */
363 int
364 nfs_inactive(struct vop_inactive_args *ap)
365 {
366         struct nfsmount *nmp = VFSTONFS(ap->a_vp->v_mount);
367         struct nfsnode *np;
368         struct sillyrename *sp;
369
370         lwkt_gettoken(&nmp->nm_token);
371
372         np = VTONFS(ap->a_vp);
373         if (prtactive && ap->a_vp->v_sysref.refcnt > 1)
374                 vprint("nfs_inactive: pushing active", ap->a_vp);
375         if (ap->a_vp->v_type != VDIR) {
376                 sp = np->n_sillyrename;
377                 np->n_sillyrename = NULL;
378         } else {
379                 sp = NULL;
380         }
381         if (sp) {
382                 /*
383                  * We need a reference to keep the vnode from being
384                  * recycled by getnewvnode while we do the I/O
385                  * associated with discarding the buffers.  The vnode
386                  * is already locked.
387                  */
388                 nfs_vinvalbuf(ap->a_vp, 0, 1);
389
390                 /*
391                  * Remove the silly file that was rename'd earlier
392                  */
393                 nfs_removeit(sp);
394                 crfree(sp->s_cred);
395                 vrele(sp->s_dvp);
396                 FREE((caddr_t)sp, M_NFSREQ);
397         }
398
399         np->n_flag &= ~(NWRITEERR | NACC | NUPD | NCHG | NLOCKED | NWANTED);
400         lwkt_reltoken(&nmp->nm_token);
401
402         return (0);
403 }
404
405 /*
406  * Reclaim an nfsnode so that it can be used for other purposes.
407  *
408  * nfs_reclaim(struct vnode *a_vp)
409  */
410 int
411 nfs_reclaim(struct vop_reclaim_args *ap)
412 {
413         struct vnode *vp = ap->a_vp;
414         struct nfsnode *np = VTONFS(vp);
415         struct nfsdmap *dp, *dp2;
416         struct nfsmount *nmp = VFSTONFS(vp->v_mount);
417
418         if (prtactive && vp->v_sysref.refcnt > 1)
419                 vprint("nfs_reclaim: pushing active", vp);
420
421
422         /*
423          * Remove from hash table
424          */
425         lwkt_gettoken(&nfsnhash_token);
426         if (np->n_hash.le_prev != NULL)
427                 LIST_REMOVE(np, n_hash);
428         lwkt_reltoken(&nfsnhash_token);
429
430         /*
431          * Free up any directory cookie structures and
432          * large file handle structures that might be associated with
433          * this nfs node.
434          */
435         lwkt_gettoken(&nmp->nm_token);
436         if (vp->v_type == VDIR) {
437                 dp = np->n_cookies.lh_first;
438                 while (dp) {
439                         dp2 = dp;
440                         dp = dp->ndm_list.le_next;
441                         FREE((caddr_t)dp2, M_NFSDIROFF);
442                 }
443         }
444         if (np->n_fhsize > NFS_SMALLFH) {
445                 FREE((caddr_t)np->n_fhp, M_NFSBIGFH);
446         }
447         if (np->n_rucred) {
448                 crfree(np->n_rucred);
449                 np->n_rucred = NULL;
450         }
451         if (np->n_wucred) {
452                 crfree(np->n_wucred);
453                 np->n_wucred = NULL;
454         }
455         vp->v_data = NULL;
456
457         lwkt_reltoken(&nmp->nm_token);
458         objcache_put(nfsnode_objcache, np);
459
460         return (0);
461 }
462