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