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