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