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