1 /* $NetBSD: coda_subr.c,v 1.16 2003/08/28 05:55:19 mrg Exp $ */ 2 3 /* 4 * 5 * Coda: an Experimental Distributed File System 6 * Release 3.1 7 * 8 * Copyright (c) 1987-1998 Carnegie Mellon University 9 * All Rights Reserved 10 * 11 * Permission to use, copy, modify and distribute this software and its 12 * documentation is hereby granted, provided that both the copyright 13 * notice and this permission notice appear in all copies of the 14 * software, derivative works or modified versions, and any portions 15 * thereof, and that both notices appear in supporting documentation, and 16 * that credit is given to Carnegie Mellon University in all documents 17 * and publicity pertaining to direct or indirect use of this code or its 18 * derivatives. 19 * 20 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS, 21 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS 22 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON 23 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER 24 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF 25 * ANY DERIVATIVE WORK. 26 * 27 * Carnegie Mellon encourages users of this software to return any 28 * improvements or extensions that they make, and to grant Carnegie 29 * Mellon the rights to redistribute these changes without encumbrance. 30 * 31 * @(#) coda/coda_subr.c,v 1.1.1.1 1998/08/29 21:26:45 rvb Exp $ 32 */ 33 34 /* 35 * Mach Operating System 36 * Copyright (c) 1989 Carnegie-Mellon University 37 * All rights reserved. The CMU software License Agreement specifies 38 * the terms and conditions for use and redistribution. 39 */ 40 41 /* 42 * This code was written for the Coda file system at Carnegie Mellon 43 * University. Contributers include David Steere, James Kistler, and 44 * M. Satyanarayanan. */ 45 46 /* NOTES: rvb 47 * 1. Added coda_unmounting to mark all cnodes as being UNMOUNTING. This has to 48 * be done before dounmount is called. Because some of the routines that 49 * dounmount calls before coda_unmounted might try to force flushes to venus. 50 * The vnode pager does this. 51 * 2. coda_unmounting marks all cnodes scanning coda_cache. 52 * 3. cfs_checkunmounting (under DEBUG) checks all cnodes by chasing the vnodes 53 * under the /coda mount point. 54 * 4. coda_cacheprint (under DEBUG) prints names with vnode/cnode address 55 */ 56 57 #include <sys/cdefs.h> 58 __KERNEL_RCSID(0, "$NetBSD: coda_subr.c,v 1.16 2003/08/28 05:55:19 mrg Exp $"); 59 60 #include <sys/param.h> 61 #include <sys/systm.h> 62 #include <sys/malloc.h> 63 #include <sys/proc.h> 64 #include <sys/select.h> 65 #include <sys/mount.h> 66 67 #include <coda/coda.h> 68 #include <coda/cnode.h> 69 #include <coda/coda_subr.h> 70 #include <coda/coda_namecache.h> 71 72 #ifdef _KERNEL_OPT 73 #include "opt_coda_compat.h" 74 #endif 75 76 int coda_active = 0; 77 int coda_reuse = 0; 78 int coda_new = 0; 79 80 struct cnode *coda_freelist = NULL; 81 struct cnode *coda_cache[CODA_CACHESIZE]; 82 83 #define CNODE_NEXT(cp) ((cp)->c_next) 84 85 #ifdef CODA_COMPAT_5 86 #define coda_hash(fid) \ 87 (((fid)->Volume + (fid)->Vnode) & (CODA_CACHESIZE-1)) 88 #define IS_DIR(cnode) (cnode.Vnode & 0x1) 89 #else 90 #define coda_hash(fid) \ 91 (coda_f2i(fid) & (CODA_CACHESIZE-1)) 92 #define IS_DIR(cnode) (cnode.opaque[2] & 0x1) 93 #endif 94 95 /* 96 * Allocate a cnode. 97 */ 98 struct cnode * 99 coda_alloc(void) 100 { 101 struct cnode *cp; 102 103 if (coda_freelist) { 104 cp = coda_freelist; 105 coda_freelist = CNODE_NEXT(cp); 106 coda_reuse++; 107 } 108 else { 109 CODA_ALLOC(cp, struct cnode *, sizeof(struct cnode)); 110 /* NetBSD vnodes don't have any Pager info in them ('cause there are 111 no external pagers, duh!) */ 112 #define VNODE_VM_INFO_INIT(vp) /* MT */ 113 VNODE_VM_INFO_INIT(CTOV(cp)); 114 coda_new++; 115 } 116 memset(cp, 0, sizeof (struct cnode)); 117 118 return(cp); 119 } 120 121 /* 122 * Deallocate a cnode. 123 */ 124 void 125 coda_free(cp) 126 struct cnode *cp; 127 { 128 129 CNODE_NEXT(cp) = coda_freelist; 130 coda_freelist = cp; 131 } 132 133 /* 134 * Put a cnode in the hash table 135 */ 136 void 137 coda_save(cp) 138 struct cnode *cp; 139 { 140 CNODE_NEXT(cp) = coda_cache[coda_hash(&cp->c_fid)]; 141 coda_cache[coda_hash(&cp->c_fid)] = cp; 142 } 143 144 /* 145 * Remove a cnode from the hash table 146 */ 147 void 148 coda_unsave(cp) 149 struct cnode *cp; 150 { 151 struct cnode *ptr; 152 struct cnode *ptrprev = NULL; 153 154 ptr = coda_cache[coda_hash(&cp->c_fid)]; 155 while (ptr != NULL) { 156 if (ptr == cp) { 157 if (ptrprev == NULL) { 158 coda_cache[coda_hash(&cp->c_fid)] 159 = CNODE_NEXT(ptr); 160 } else { 161 CNODE_NEXT(ptrprev) = CNODE_NEXT(ptr); 162 } 163 CNODE_NEXT(cp) = (struct cnode *)NULL; 164 165 return; 166 } 167 ptrprev = ptr; 168 ptr = CNODE_NEXT(ptr); 169 } 170 } 171 172 /* 173 * Lookup a cnode by fid. If the cnode is dying, it is bogus so skip it. 174 * NOTE: this allows multiple cnodes with same fid -- dcs 1/25/95 175 */ 176 struct cnode * 177 coda_find(fid) 178 CodaFid *fid; 179 { 180 struct cnode *cp; 181 182 cp = coda_cache[coda_hash(fid)]; 183 while (cp) { 184 if (coda_fid_eq(&(cp->c_fid), fid) && 185 (!IS_UNMOUNTING(cp))) 186 { 187 coda_active++; 188 return(cp); 189 } 190 cp = CNODE_NEXT(cp); 191 } 192 return(NULL); 193 } 194 195 /* 196 * coda_kill is called as a side effect to vcopen. To prevent any 197 * cnodes left around from an earlier run of a venus or warden from 198 * causing problems with the new instance, mark any outstanding cnodes 199 * as dying. Future operations on these cnodes should fail (excepting 200 * coda_inactive of course!). Since multiple venii/wardens can be 201 * running, only kill the cnodes for a particular entry in the 202 * coda_mnttbl. -- DCS 12/1/94 */ 203 204 int 205 coda_kill(whoIam, dcstat) 206 struct mount *whoIam; 207 enum dc_status dcstat; 208 { 209 int hash, count = 0; 210 struct cnode *cp; 211 212 /* 213 * Algorithm is as follows: 214 * Second, flush whatever vnodes we can from the name cache. 215 * 216 * Finally, step through whatever is left and mark them dying. 217 * This prevents any operation at all. 218 219 */ 220 221 /* This is slightly overkill, but should work. Eventually it'd be 222 * nice to only flush those entries from the namecache that 223 * reference a vnode in this vfs. */ 224 coda_nc_flush(dcstat); 225 226 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 227 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 228 if (CTOV(cp)->v_mount == whoIam) { 229 #ifdef DEBUG 230 printf("coda_kill: vp %p, cp %p\n", CTOV(cp), cp); 231 #endif 232 count++; 233 CODADEBUG(CODA_FLUSH, 234 myprintf(("Live cnode fid %s flags %d count %d\n", 235 coda_f2s(&cp->c_fid), 236 cp->c_flags, 237 CTOV(cp)->v_usecount)); ); 238 } 239 } 240 } 241 return count; 242 } 243 244 /* 245 * There are two reasons why a cnode may be in use, it may be in the 246 * name cache or it may be executing. 247 */ 248 void 249 coda_flush(dcstat) 250 enum dc_status dcstat; 251 { 252 int hash; 253 struct cnode *cp; 254 255 coda_clstat.ncalls++; 256 coda_clstat.reqs[CODA_FLUSH]++; 257 258 coda_nc_flush(dcstat); /* flush files from the name cache */ 259 260 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 261 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 262 if (!IS_DIR(cp->c_fid)) /* only files can be executed */ 263 coda_vmflush(cp); 264 } 265 } 266 } 267 268 /* 269 * As a debugging measure, print out any cnodes that lived through a 270 * name cache flush. 271 */ 272 void 273 coda_testflush(void) 274 { 275 int hash; 276 struct cnode *cp; 277 278 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 279 for (cp = coda_cache[hash]; 280 cp != NULL; 281 cp = CNODE_NEXT(cp)) { 282 myprintf(("Live cnode fid %s count %d\n", 283 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount)); 284 } 285 } 286 } 287 288 /* 289 * First, step through all cnodes and mark them unmounting. 290 * NetBSD kernels may try to fsync them now that venus 291 * is dead, which would be a bad thing. 292 * 293 */ 294 void 295 coda_unmounting(whoIam) 296 struct mount *whoIam; 297 { 298 int hash; 299 struct cnode *cp; 300 301 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 302 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 303 if (CTOV(cp)->v_mount == whoIam) { 304 if (cp->c_flags & (C_LOCKED|C_WANTED)) { 305 printf("coda_unmounting: Unlocking %p\n", cp); 306 cp->c_flags &= ~(C_LOCKED|C_WANTED); 307 wakeup((caddr_t) cp); 308 } 309 cp->c_flags |= C_UNMOUNTING; 310 } 311 } 312 } 313 } 314 315 #ifdef DEBUG 316 void 317 coda_checkunmounting(mp) 318 struct mount *mp; 319 { 320 struct vnode *vp, *nvp; 321 struct cnode *cp; 322 int count = 0, bad = 0; 323 loop: 324 for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) { 325 if (vp->v_mount != mp) 326 goto loop; 327 nvp = vp->v_mntvnodes.le_next; 328 cp = VTOC(vp); 329 count++; 330 if (!(cp->c_flags & C_UNMOUNTING)) { 331 bad++; 332 printf("vp %p, cp %p missed\n", vp, cp); 333 cp->c_flags |= C_UNMOUNTING; 334 } 335 } 336 } 337 338 void 339 coda_cacheprint(whoIam) 340 struct mount *whoIam; 341 { 342 int hash; 343 struct cnode *cp; 344 int count = 0; 345 346 printf("coda_cacheprint: coda_ctlvp %p, cp %p", coda_ctlvp, VTOC(coda_ctlvp)); 347 coda_nc_name(VTOC(coda_ctlvp)); 348 printf("\n"); 349 350 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 351 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 352 if (CTOV(cp)->v_mount == whoIam) { 353 printf("coda_cacheprint: vp %p, cp %p", CTOV(cp), cp); 354 coda_nc_name(cp); 355 printf("\n"); 356 count++; 357 } 358 } 359 } 360 printf("coda_cacheprint: count %d\n", count); 361 } 362 #endif 363 364 /* 365 * There are 6 cases where invalidations occur. The semantics of each 366 * is listed here. 367 * 368 * CODA_FLUSH -- flush all entries from the name cache and the cnode cache. 369 * CODA_PURGEUSER -- flush all entries from the name cache for a specific user 370 * This call is a result of token expiration. 371 * 372 * The next two are the result of callbacks on a file or directory. 373 * CODA_ZAPDIR -- flush the attributes for the dir from its cnode. 374 * Zap all children of this directory from the namecache. 375 * CODA_ZAPFILE -- flush the attributes for a file. 376 * 377 * The fifth is a result of Venus detecting an inconsistent file. 378 * CODA_PURGEFID -- flush the attribute for the file 379 * If it is a dir (odd vnode), purge its 380 * children from the namecache 381 * remove the file from the namecache. 382 * 383 * The sixth allows Venus to replace local fids with global ones 384 * during reintegration. 385 * 386 * CODA_REPLACE -- replace one CodaFid with another throughout the name cache 387 */ 388 389 int handleDownCall(opcode, out) 390 int opcode; union outputArgs *out; 391 { 392 int error; 393 394 /* Handle invalidate requests. */ 395 switch (opcode) { 396 case CODA_FLUSH : { 397 398 coda_flush(IS_DOWNCALL); 399 400 CODADEBUG(CODA_FLUSH,coda_testflush();) /* print remaining cnodes */ 401 return(0); 402 } 403 404 case CODA_PURGEUSER : { 405 coda_clstat.ncalls++; 406 coda_clstat.reqs[CODA_PURGEUSER]++; 407 408 /* XXX - need to prevent fsync's */ 409 #ifdef CODA_COMPAT_5 410 coda_nc_purge_user(out->coda_purgeuser.cred.cr_uid, IS_DOWNCALL); 411 #else 412 coda_nc_purge_user(out->coda_purgeuser.uid, IS_DOWNCALL); 413 #endif 414 return(0); 415 } 416 417 case CODA_ZAPFILE : { 418 struct cnode *cp; 419 420 error = 0; 421 coda_clstat.ncalls++; 422 coda_clstat.reqs[CODA_ZAPFILE]++; 423 424 cp = coda_find(&out->coda_zapfile.Fid); 425 if (cp != NULL) { 426 vref(CTOV(cp)); 427 428 cp->c_flags &= ~C_VATTR; 429 if (CTOV(cp)->v_flag & VTEXT) 430 error = coda_vmflush(cp); 431 CODADEBUG(CODA_ZAPFILE, myprintf(( 432 "zapfile: fid = %s, refcnt = %d, error = %d\n", 433 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount - 1, error));); 434 if (CTOV(cp)->v_usecount == 1) { 435 cp->c_flags |= C_PURGING; 436 } 437 vrele(CTOV(cp)); 438 } 439 440 return(error); 441 } 442 443 case CODA_ZAPDIR : { 444 struct cnode *cp; 445 446 coda_clstat.ncalls++; 447 coda_clstat.reqs[CODA_ZAPDIR]++; 448 449 cp = coda_find(&out->coda_zapdir.Fid); 450 if (cp != NULL) { 451 vref(CTOV(cp)); 452 453 cp->c_flags &= ~C_VATTR; 454 coda_nc_zapParentfid(&out->coda_zapdir.Fid, IS_DOWNCALL); 455 456 CODADEBUG(CODA_ZAPDIR, myprintf(( 457 "zapdir: fid = %s, refcnt = %d\n", 458 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount - 1));); 459 if (CTOV(cp)->v_usecount == 1) { 460 cp->c_flags |= C_PURGING; 461 } 462 vrele(CTOV(cp)); 463 } 464 465 return(0); 466 } 467 468 case CODA_PURGEFID : { 469 struct cnode *cp; 470 471 error = 0; 472 coda_clstat.ncalls++; 473 coda_clstat.reqs[CODA_PURGEFID]++; 474 475 cp = coda_find(&out->coda_purgefid.Fid); 476 if (cp != NULL) { 477 vref(CTOV(cp)); 478 if (IS_DIR(out->coda_purgefid.Fid)) { /* Vnode is a directory */ 479 coda_nc_zapParentfid(&out->coda_purgefid.Fid, 480 IS_DOWNCALL); 481 } 482 cp->c_flags &= ~C_VATTR; 483 coda_nc_zapfid(&out->coda_purgefid.Fid, IS_DOWNCALL); 484 if (!(IS_DIR(out->coda_purgefid.Fid)) 485 && (CTOV(cp)->v_flag & VTEXT)) { 486 487 error = coda_vmflush(cp); 488 } 489 CODADEBUG(CODA_PURGEFID, myprintf(( 490 "purgefid: fid = %s, refcnt = %d, error = %d\n", 491 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount - 1, error));); 492 if (CTOV(cp)->v_usecount == 1) { 493 cp->c_flags |= C_PURGING; 494 } 495 vrele(CTOV(cp)); 496 } 497 return(error); 498 } 499 500 case CODA_REPLACE : { 501 struct cnode *cp = NULL; 502 503 coda_clstat.ncalls++; 504 coda_clstat.reqs[CODA_REPLACE]++; 505 506 cp = coda_find(&out->coda_replace.OldFid); 507 if (cp != NULL) { 508 /* remove the cnode from the hash table, replace the fid, and reinsert */ 509 vref(CTOV(cp)); 510 coda_unsave(cp); 511 cp->c_fid = out->coda_replace.NewFid; 512 coda_save(cp); 513 514 CODADEBUG(CODA_REPLACE, myprintf(( 515 "replace: oldfid = %s, newfid = %s, cp = %p\n", 516 coda_f2s(&out->coda_replace.OldFid), 517 coda_f2s(&cp->c_fid), cp));) 518 vrele(CTOV(cp)); 519 } 520 return (0); 521 } 522 default: 523 myprintf(("handleDownCall: unknown opcode %d\n", opcode)); 524 return (EINVAL); 525 } 526 } 527 528 /* coda_grab_vnode: lives in either cfs_mach.c or cfs_nbsd.c */ 529 530 int 531 coda_vmflush(cp) 532 struct cnode *cp; 533 { 534 return 0; 535 } 536 537 538 /* 539 * kernel-internal debugging switches 540 */ 541 542 void coda_debugon(void) 543 { 544 codadebug = -1; 545 coda_nc_debug = -1; 546 coda_vnop_print_entry = 1; 547 coda_psdev_print_entry = 1; 548 coda_vfsop_print_entry = 1; 549 } 550 551 void coda_debugoff(void) 552 { 553 codadebug = 0; 554 coda_nc_debug = 0; 555 coda_vnop_print_entry = 0; 556 coda_psdev_print_entry = 0; 557 coda_vfsop_print_entry = 0; 558 } 559 560 /* 561 * Utilities used by both client and server 562 * Standard levels: 563 * 0) no debugging 564 * 1) hard failures 565 * 2) soft failures 566 * 3) current test software 567 * 4) main procedure entry points 568 * 5) main procedure exit points 569 * 6) utility procedure entry points 570 * 7) utility procedure exit points 571 * 8) obscure procedure entry points 572 * 9) obscure procedure exit points 573 * 10) random stuff 574 * 11) all <= 1 575 * 12) all <= 2 576 * 13) all <= 3 577 * ... 578 */ 579