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