xref: /dragonfly/sys/vfs/nfs/nfs_node.c (revision 5b3b84424f6bfcb5eb06ad9dcd7a29cfd895a39e)
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. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  *        @(#)nfs_node.c      8.6 (Berkeley) 5/22/95
33  * $FreeBSD: src/sys/nfs/nfs_node.c,v 1.36.2.3 2002/01/05 22:25:04 dillon Exp $
34  */
35 
36 
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/proc.h>
40 #include <sys/mount.h>
41 #include <sys/vnode.h>
42 #include <sys/malloc.h>
43 #include <sys/kernel.h>
44 #include <sys/fnv_hash.h>
45 #include <sys/objcache.h>
46 
47 #include "rpcv2.h"
48 #include "nfsproto.h"
49 #include "nfs.h"
50 #include "nfsmount.h"
51 #include "nfsnode.h"
52 
53 static struct lwkt_token nfsnhash_token =
54                               LWKT_TOKEN_INITIALIZER(nfsnhash_token);
55 static struct lock nfsnhash_lock;
LIST_HEAD(nfsnodehashhead,nfsnode)56 __read_mostly static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
57 __read_mostly static u_long nfsnodehash;
58 
59 #define TRUE        1
60 #define   FALSE     0
61 
62 #define NFSNOHASH(fhsum)      (&nfsnodehashtbl[(fhsum) & nfsnodehash])
63 
64 /*
65  * Initialize hash links for nfsnodes
66  * and build nfsnode free list.
67  */
68 void
69 nfs_nhinit(void)
70 {
71           int hsize = vfs_inodehashsize();
72 
73           nfsnodehashtbl = hashinit(hsize, M_NFS, &nfsnodehash);
74           lockinit(&nfsnhash_lock, "nfsnht", 0, 0);
75 }
76 
77 void
nfs_nhdestroy(void)78 nfs_nhdestroy(void)
79 {
80           hashdestroy(nfsnodehashtbl, M_NFS, nfsnodehash);
81 }
82 
83 /*
84  * Look up a vnode/nfsnode by file handle.
85  * Callers must check for mount points!!
86  * In all cases, a pointer to a
87  * nfsnode structure is returned.
88  */
89 
90 int
nfs_nget(struct mount * mntp,nfsfh_t * fhp,int fhsize,struct nfsnode ** npp,struct vnode * notvp)91 nfs_nget(struct mount *mntp, nfsfh_t *fhp, int fhsize, struct nfsnode **npp,
92            struct vnode *notvp)
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           LIST_FOREACH(np, nhpp, n_hash) {
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 (vp == notvp) {
123                               kprintf("nfs warning: client-client collision "
124                                         "during rename/link/softlink\n");
125                               *npp = NULL;
126                               lwkt_reltoken(&nfsnhash_token);
127                               return (ESTALE);
128                     }
129                     if (vget(vp, LK_EXCLUSIVE))
130                               goto loop;
131                     LIST_FOREACH(np, nhpp, n_hash) {
132                               if (mntp == NFSTOV(np)->v_mount &&
133                                   np->n_fhsize == fhsize &&
134                                   bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize) == 0
135                               ) {
136                                         break;
137                               }
138                     }
139                     if (np == NULL || NFSTOV(np) != vp) {
140                               vput(vp);
141                               goto loop;
142                     }
143                     *npp = np;
144                     lwkt_reltoken(&nfsnhash_token);
145                     return(0);
146           }
147 
148           /*
149            * Obtain a lock to prevent a race condition if the getnewvnode()
150            * or MALLOC() below happens to block.
151            */
152           if (lockmgr(&nfsnhash_lock, LK_EXCLUSIVE | LK_SLEEPFAIL))
153                     goto loop;
154 
155           /*
156            * Allocate before getnewvnode since doing so afterward
157            * might cause a bogus v_data pointer to get dereferenced
158            * elsewhere if objcache should block.
159            */
160           np = kmalloc_obj(sizeof(struct nfsnode), nmp->nm_mnode,
161                                M_WAITOK|M_ZERO);
162 
163           error = getnewvnode(VT_NFS, mntp, &vp, 0, 0);
164           if (error) {
165                     lockmgr(&nfsnhash_lock, LK_RELEASE);
166                     *npp = NULL;
167                     kfree_obj(np, nmp->nm_mnode);
168                     lwkt_reltoken(&nfsnhash_token);
169                     return (error);
170           }
171 
172           /*
173            * Initialize most of (np).
174            */
175           bzero(np, sizeof (*np));
176           if (fhsize > NFS_SMALLFH) {
177                     np->n_fhp = kmalloc(fhsize, M_NFSBIGFH, M_WAITOK);
178           } else {
179                     np->n_fhp = &np->n_fh;
180           }
181           bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
182           np->n_fhsize = fhsize;
183           lockinit(&np->n_rslock, "nfrslk", 0, lkflags);
184 
185           /*
186            * Validate that we did not race another nfs_nget() due to blocking
187            * here and there.
188            */
189           for (np2 = nhpp->lh_first; np2 != NULL; np2 = np2->n_hash.le_next) {
190                     if (mntp != NFSTOV(np2)->v_mount || np2->n_fhsize != fhsize ||
191                         bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize)) {
192                               continue;
193                     }
194                     vx_put(vp);
195                     lockmgr(&nfsnhash_lock, LK_RELEASE);
196 
197                     if (np->n_fhsize > NFS_SMALLFH)
198                               kfree((caddr_t)np->n_fhp, M_NFSBIGFH);
199                     np->n_fhp = NULL;
200                     kfree_obj(np, nmp->nm_mnode);
201                     goto retry;
202           }
203 
204           /*
205            * Finish connecting up (np, vp) and insert the nfsnode in the
206            * hash for its new file handle.
207            *
208            * nvp is locked & refd so effectively so is np.
209            */
210           np->n_vnode = vp;
211           vp->v_data = np;
212           LIST_INSERT_HEAD(nhpp, np, n_hash);
213           *npp = np;
214           lockmgr(&nfsnhash_lock, LK_RELEASE);
215           lwkt_reltoken(&nfsnhash_token);
216           vx_downgrade(vp);
217 
218           return (0);
219 }
220 
221 /*
222  * Nonblocking version of nfs_nget()
223  */
224 int
nfs_nget_nonblock(struct mount * mntp,nfsfh_t * fhp,int fhsize,struct nfsnode ** npp,struct vnode * notvp)225 nfs_nget_nonblock(struct mount *mntp, nfsfh_t *fhp, int fhsize,
226                       struct nfsnode **npp, struct vnode *notvp)
227 {
228           struct nfsnode *np, *np2;
229           struct nfsnodehashhead *nhpp;
230           struct vnode *vp;
231           int error;
232           int lkflags;
233           struct nfsmount *nmp;
234 
235           /*
236            * Calculate nfs mount point and figure out whether the rslock should
237            * be interruptable or not.
238            */
239           nmp = VFSTONFS(mntp);
240           if (nmp->nm_flag & NFSMNT_INT)
241                     lkflags = LK_PCATCH;
242           else
243                     lkflags = 0;
244           vp = NULL;
245           *npp = NULL;
246 
247           lwkt_gettoken(&nfsnhash_token);
248 
249 retry:
250           nhpp = NFSNOHASH(fnv_32_buf(fhp->fh_bytes, fhsize, FNV1_32_INIT));
251 loop:
252           LIST_FOREACH(np, nhpp, n_hash) {
253                     if (mntp != NFSTOV(np)->v_mount || np->n_fhsize != fhsize ||
254                         bcmp((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize)) {
255                               continue;
256                     }
257                     if (vp == NULL) {
258                               vp = NFSTOV(np);
259                               if (vp == notvp) {
260                                         kprintf("nfs warning: client-client collision "
261                                                   "during rename/link/softlink\n");
262                                         error = ESTALE;
263                                         goto fail;
264                               }
265                               if (vget(vp, LK_EXCLUSIVE | LK_NOWAIT)) {
266                                         error = EWOULDBLOCK;
267                                         goto fail;
268                               }
269                               goto loop;
270                     }
271                     if (NFSTOV(np) != vp) {
272                               vput(vp);
273                               goto loop;
274                     }
275                     *npp = np;
276                     lwkt_reltoken(&nfsnhash_token);
277                     return(0);
278           }
279 
280           /*
281            * Not found.  If we raced and had acquired a vp we have to release
282            * it here.
283            */
284           if (vp) {
285                     vput(vp);
286                     vp = NULL;
287           }
288 
289           /*
290            * Obtain a lock to prevent a race condition if the getnewvnode()
291            * or MALLOC() below happens to block.
292            */
293           if (lockmgr(&nfsnhash_lock, LK_EXCLUSIVE | LK_SLEEPFAIL))
294                     goto loop;
295 
296           /*
297            * Entry not found, allocate a new entry.
298            *
299            * Allocate before getnewvnode since doing so afterward
300            * might cause a bogus v_data pointer to get dereferenced
301            * elsewhere if objcache should block.
302            */
303           np = kmalloc_obj(sizeof(struct nfsnode), nmp->nm_mnode,
304                                M_WAITOK|M_ZERO);
305 
306           error = getnewvnode(VT_NFS, mntp, &vp, 0, 0);
307           if (error) {
308                     lockmgr(&nfsnhash_lock, LK_RELEASE);
309                     kfree_obj(np, nmp->nm_mnode);
310                     lwkt_reltoken(&nfsnhash_token);
311                     return (error);
312           }
313 
314           /*
315            * Initialize most of (np).
316            */
317           bzero(np, sizeof (*np));
318           if (fhsize > NFS_SMALLFH) {
319                     np->n_fhp = kmalloc(fhsize, M_NFSBIGFH, M_WAITOK);
320           } else {
321                     np->n_fhp = &np->n_fh;
322           }
323           bcopy((caddr_t)fhp, (caddr_t)np->n_fhp, fhsize);
324           np->n_fhsize = fhsize;
325           lockinit(&np->n_rslock, "nfrslk", 0, lkflags);
326 
327           /*
328            * Validate that we did not race another nfs_nget() due to blocking
329            * here and there.
330            */
331           for (np2 = nhpp->lh_first; np2 != NULL; np2 = np2->n_hash.le_next) {
332                     if (mntp != NFSTOV(np2)->v_mount || np2->n_fhsize != fhsize ||
333                         bcmp((caddr_t)fhp, (caddr_t)np2->n_fhp, fhsize)) {
334                               continue;
335                     }
336                     vx_put(vp);
337                     lockmgr(&nfsnhash_lock, LK_RELEASE);
338 
339                     if (np->n_fhsize > NFS_SMALLFH)
340                               kfree((caddr_t)np->n_fhp, M_NFSBIGFH);
341                     np->n_fhp = NULL;
342                     kfree_obj(np, nmp->nm_mnode);
343 
344                     /*
345                      * vp state is retained on retry/loop so we must NULL it
346                      * out here or fireworks may ensue.
347                      */
348                     vp = NULL;
349                     goto retry;
350           }
351 
352           /*
353            * Finish connecting up (np, vp) and insert the nfsnode in the
354            * hash for its new file handle.
355            *
356            * nvp is locked & refd so effectively so is np.
357            */
358           np->n_vnode = vp;
359           vp->v_data = np;
360           LIST_INSERT_HEAD(nhpp, np, n_hash);
361 
362           /*
363            * nvp is locked & refd so effectively so is np.
364            */
365           *npp = np;
366           error = 0;
367           lockmgr(&nfsnhash_lock, LK_RELEASE);
368           vx_downgrade(vp);
369 fail:
370           lwkt_reltoken(&nfsnhash_token);
371           return (error);
372 }
373 
374 /*
375  * nfs_inactive(struct vnode *a_vp)
376  *
377  * NOTE: the passed vnode is locked but not referenced.  On return the
378  * vnode must be unlocked and not referenced.
379  */
380 int
nfs_inactive(struct vop_inactive_args * ap)381 nfs_inactive(struct vop_inactive_args *ap)
382 {
383           struct nfsmount *nmp = VFSTONFS(ap->a_vp->v_mount);
384           struct nfsnode *np;
385           struct sillyrename *sp;
386 
387           lwkt_gettoken(&nmp->nm_token);
388 
389           np = VTONFS(ap->a_vp);
390           if (prtactive && VREFCNT(ap->a_vp) > 1)
391                     vprint("nfs_inactive: pushing active", ap->a_vp);
392           if (ap->a_vp->v_type != VDIR) {
393                     sp = np->n_sillyrename;
394                     np->n_sillyrename = NULL;
395           } else {
396                     sp = NULL;
397           }
398           if (sp) {
399                     /*
400                      * We need a reference to keep the vnode from being
401                      * recycled by getnewvnode while we do the I/O
402                      * associated with discarding the buffers.  The vnode
403                      * is already locked.
404                      */
405                     nfs_vinvalbuf(ap->a_vp, 0, 1);
406 
407                     /*
408                      * Remove the silly file that was rename'd earlier
409                      */
410                     nfs_removeit(sp);
411                     crfree(sp->s_cred);
412                     vrele(sp->s_dvp);
413                     kfree((caddr_t)sp, M_NFSREQ);
414           }
415 
416           np->n_flag &= ~(NWRITEERR | NACC | NUPD | NCHG | NLOCKED | NWANTED);
417           if (np->n_flag & NREMOVED)
418                     vrecycle(ap->a_vp);
419           lwkt_reltoken(&nmp->nm_token);
420 
421           return (0);
422 }
423 
424 /*
425  * Reclaim an nfsnode so that it can be used for other purposes.
426  *
427  * There should be no direct references to the related nfs node
428  * since nobody is holding the vnode any more, other than hash
429  * lookups which are interlocked against nfsnhash_token and vget().
430  *
431  * nfs_reclaim(struct vnode *a_vp)
432  */
433 int
nfs_reclaim(struct vop_reclaim_args * ap)434 nfs_reclaim(struct vop_reclaim_args *ap)
435 {
436           struct vnode *vp = ap->a_vp;
437           struct nfsnode *np = VTONFS(vp);
438           struct nfsdmap *dp, *dp2;
439           struct nfsmount *nmp = VFSTONFS(vp->v_mount);
440 
441           if (prtactive && VREFCNT(vp) > 1)
442                     vprint("nfs_reclaim: pushing active", vp);
443 
444 
445           /*
446            * Remove from hash table and remove the cross links.
447            *
448            * NOTE: Other NFS code may look up a np and vget() the
449            *         related vnode, then will check np->n_vnode.
450            *         We must clear np->n_vnode here to ensure that all
451            *         possible races are dealt with.
452            */
453           lwkt_gettoken(&nfsnhash_token);
454           KKASSERT(np->n_vnode == vp);
455           if (np->n_hash.le_prev != NULL)
456                     LIST_REMOVE(np, n_hash);
457           np->n_vnode = NULL;
458           vp->v_data = NULL;
459           lwkt_reltoken(&nfsnhash_token);
460 
461           /*
462            * Free up any directory cookie structures and
463            * large file handle structures that might be associated with
464            * this nfs node.
465            */
466           if (vp->v_type == VDIR) {
467                     dp = np->n_cookies.lh_first;
468                     while (dp) {
469                               dp2 = dp;
470                               dp = dp->ndm_list.le_next;
471                               kfree((caddr_t)dp2, M_NFSDIROFF);
472                     }
473           }
474           if (np->n_fhsize > NFS_SMALLFH) {
475                     kfree((caddr_t)np->n_fhp, M_NFSBIGFH);
476           }
477           if (np->n_rucred) {
478                     crfree(np->n_rucred);
479                     np->n_rucred = NULL;
480           }
481           if (np->n_wucred) {
482                     crfree(np->n_wucred);
483                     np->n_wucred = NULL;
484           }
485           kfree_obj(np, nmp->nm_mnode);
486 
487           return (0);
488 }
489 
490