xref: /dragonfly/sys/vfs/nfs/nfs_node.c (revision 0b2c5ee3)
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;
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
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
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
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
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
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