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