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