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