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