2 * Copyright (c) 1989, 1993
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Rick Macklem at The University of Guelph.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
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 $
37 #include <sys/param.h>
38 #include <sys/systm.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>
53 static struct lwkt_token nfsnhash_token
=
54 LWKT_TOKEN_INITIALIZER(nfsnhash_token
);
55 static struct lock nfsnhash_lock
;
56 __read_mostly
static LIST_HEAD(nfsnodehashhead
, nfsnode
) *nfsnodehashtbl
;
57 __read_mostly
static u_long nfsnodehash
;
62 #define NFSNOHASH(fhsum) (&nfsnodehashtbl[(fhsum) & nfsnodehash])
65 * Initialize hash links for nfsnodes
66 * and build nfsnode free list.
71 int hsize
= vfs_inodehashsize();
73 nfsnodehashtbl
= hashinit(hsize
, M_NFS
, &nfsnodehash
);
74 lockinit(&nfsnhash_lock
, "nfsnht", 0, 0);
80 hashdestroy(nfsnodehashtbl
, M_NFS
, nfsnodehash
);
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.
91 nfs_nget(struct mount
*mntp
, nfsfh_t
*fhp
, int fhsize
, struct nfsnode
**npp
,
94 struct nfsnode
*np
, *np2
;
95 struct nfsnodehashhead
*nhpp
;
102 * Calculate nfs mount point and figure out whether the rslock should
103 * be interruptable or not.
105 nmp
= VFSTONFS(mntp
);
106 if (nmp
->nm_flag
& NFSMNT_INT
)
111 lwkt_gettoken(&nfsnhash_token
);
114 nhpp
= NFSNOHASH(fnv_32_buf(fhp
->fh_bytes
, fhsize
, FNV1_32_INIT
));
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
)) {
123 kprintf("nfs warning: client-client collision "
124 "during rename/link/softlink\n");
126 lwkt_reltoken(&nfsnhash_token
);
129 if (vget(vp
, LK_EXCLUSIVE
))
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
139 if (np
== NULL
|| NFSTOV(np
) != vp
) {
144 lwkt_reltoken(&nfsnhash_token
);
149 * Obtain a lock to prevent a race condition if the getnewvnode()
150 * or MALLOC() below happens to block.
152 if (lockmgr(&nfsnhash_lock
, LK_EXCLUSIVE
| LK_SLEEPFAIL
))
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.
160 np
= kmalloc_obj(sizeof(struct nfsnode
), nmp
->nm_mnode
,
163 error
= getnewvnode(VT_NFS
, mntp
, &vp
, 0, 0);
165 lockmgr(&nfsnhash_lock
, LK_RELEASE
);
167 kfree_obj(np
, nmp
->nm_mnode
);
168 lwkt_reltoken(&nfsnhash_token
);
173 * Initialize most of (np).
175 bzero(np
, sizeof (*np
));
176 if (fhsize
> NFS_SMALLFH
) {
177 np
->n_fhp
= kmalloc(fhsize
, M_NFSBIGFH
, M_WAITOK
);
179 np
->n_fhp
= &np
->n_fh
;
181 bcopy((caddr_t
)fhp
, (caddr_t
)np
->n_fhp
, fhsize
);
182 np
->n_fhsize
= fhsize
;
183 lockinit(&np
->n_rslock
, "nfrslk", 0, lkflags
);
186 * Validate that we did not race another nfs_nget() due to blocking
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
)) {
195 lockmgr(&nfsnhash_lock
, LK_RELEASE
);
197 if (np
->n_fhsize
> NFS_SMALLFH
)
198 kfree((caddr_t
)np
->n_fhp
, M_NFSBIGFH
);
200 kfree_obj(np
, nmp
->nm_mnode
);
205 * Finish connecting up (np, vp) and insert the nfsnode in the
206 * hash for its new file handle.
208 * nvp is locked & refd so effectively so is np.
212 LIST_INSERT_HEAD(nhpp
, np
, n_hash
);
214 lockmgr(&nfsnhash_lock
, LK_RELEASE
);
215 lwkt_reltoken(&nfsnhash_token
);
222 * Nonblocking version of nfs_nget()
225 nfs_nget_nonblock(struct mount
*mntp
, nfsfh_t
*fhp
, int fhsize
,
226 struct nfsnode
**npp
, struct vnode
*notvp
)
228 struct nfsnode
*np
, *np2
;
229 struct nfsnodehashhead
*nhpp
;
233 struct nfsmount
*nmp
;
236 * Calculate nfs mount point and figure out whether the rslock should
237 * be interruptable or not.
239 nmp
= VFSTONFS(mntp
);
240 if (nmp
->nm_flag
& NFSMNT_INT
)
247 lwkt_gettoken(&nfsnhash_token
);
250 nhpp
= NFSNOHASH(fnv_32_buf(fhp
->fh_bytes
, fhsize
, FNV1_32_INIT
));
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
)) {
260 kprintf("nfs warning: client-client collision "
261 "during rename/link/softlink\n");
265 if (vget(vp
, LK_EXCLUSIVE
| LK_NOWAIT
)) {
271 if (NFSTOV(np
) != vp
) {
276 lwkt_reltoken(&nfsnhash_token
);
281 * Not found. If we raced and had acquired a vp we have to release
290 * Obtain a lock to prevent a race condition if the getnewvnode()
291 * or MALLOC() below happens to block.
293 if (lockmgr(&nfsnhash_lock
, LK_EXCLUSIVE
| LK_SLEEPFAIL
))
297 * Entry not found, allocate a new entry.
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.
303 np
= kmalloc_obj(sizeof(struct nfsnode
), nmp
->nm_mnode
,
306 error
= getnewvnode(VT_NFS
, mntp
, &vp
, 0, 0);
308 lockmgr(&nfsnhash_lock
, LK_RELEASE
);
309 kfree_obj(np
, nmp
->nm_mnode
);
310 lwkt_reltoken(&nfsnhash_token
);
315 * Initialize most of (np).
317 bzero(np
, sizeof (*np
));
318 if (fhsize
> NFS_SMALLFH
) {
319 np
->n_fhp
= kmalloc(fhsize
, M_NFSBIGFH
, M_WAITOK
);
321 np
->n_fhp
= &np
->n_fh
;
323 bcopy((caddr_t
)fhp
, (caddr_t
)np
->n_fhp
, fhsize
);
324 np
->n_fhsize
= fhsize
;
325 lockinit(&np
->n_rslock
, "nfrslk", 0, lkflags
);
328 * Validate that we did not race another nfs_nget() due to blocking
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
)) {
337 lockmgr(&nfsnhash_lock
, LK_RELEASE
);
339 if (np
->n_fhsize
> NFS_SMALLFH
)
340 kfree((caddr_t
)np
->n_fhp
, M_NFSBIGFH
);
342 kfree_obj(np
, nmp
->nm_mnode
);
345 * vp state is retained on retry/loop so we must NULL it
346 * out here or fireworks may ensue.
353 * Finish connecting up (np, vp) and insert the nfsnode in the
354 * hash for its new file handle.
356 * nvp is locked & refd so effectively so is np.
360 LIST_INSERT_HEAD(nhpp
, np
, n_hash
);
363 * nvp is locked & refd so effectively so is np.
367 lockmgr(&nfsnhash_lock
, LK_RELEASE
);
370 lwkt_reltoken(&nfsnhash_token
);
375 * nfs_inactive(struct vnode *a_vp)
377 * NOTE: the passed vnode is locked but not referenced. On return the
378 * vnode must be unlocked and not referenced.
381 nfs_inactive(struct vop_inactive_args
*ap
)
383 struct nfsmount
*nmp
= VFSTONFS(ap
->a_vp
->v_mount
);
385 struct sillyrename
*sp
;
387 lwkt_gettoken(&nmp
->nm_token
);
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
;
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
405 nfs_vinvalbuf(ap
->a_vp
, 0, 1);
408 * Remove the silly file that was rename'd earlier
413 kfree((caddr_t
)sp
, M_NFSREQ
);
416 np
->n_flag
&= ~(NWRITEERR
| NACC
| NUPD
| NCHG
| NLOCKED
| NWANTED
);
417 if (np
->n_flag
& NREMOVED
)
419 lwkt_reltoken(&nmp
->nm_token
);
425 * Reclaim an nfsnode so that it can be used for other purposes.
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().
431 * nfs_reclaim(struct vnode *a_vp)
434 nfs_reclaim(struct vop_reclaim_args
*ap
)
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
);
441 if (prtactive
&& VREFCNT(vp
) > 1)
442 vprint("nfs_reclaim: pushing active", vp
);
446 * Remove from hash table and remove the cross links.
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.
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
);
459 lwkt_reltoken(&nfsnhash_token
);
462 * Free up any directory cookie structures and
463 * large file handle structures that might be associated with
466 if (vp
->v_type
== VDIR
) {
467 dp
= np
->n_cookies
.lh_first
;
470 dp
= dp
->ndm_list
.le_next
;
471 kfree((caddr_t
)dp2
, M_NFSDIROFF
);
474 if (np
->n_fhsize
> NFS_SMALLFH
) {
475 kfree((caddr_t
)np
->n_fhp
, M_NFSBIGFH
);
478 crfree(np
->n_rucred
);
482 crfree(np
->n_wucred
);
485 kfree_obj(np
, nmp
->nm_mnode
);