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