1 /* $NetBSD: coda_subr.c,v 1.19 2005/12/11 12:19:50 christos 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.19 2005/12/11 12:19:50 christos 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(struct cnode *cp) 126 { 127 128 CNODE_NEXT(cp) = coda_freelist; 129 coda_freelist = cp; 130 } 131 132 /* 133 * Put a cnode in the hash table 134 */ 135 void 136 coda_save(struct cnode *cp) 137 { 138 CNODE_NEXT(cp) = coda_cache[coda_hash(&cp->c_fid)]; 139 coda_cache[coda_hash(&cp->c_fid)] = cp; 140 } 141 142 /* 143 * Remove a cnode from the hash table 144 */ 145 void 146 coda_unsave(struct cnode *cp) 147 { 148 struct cnode *ptr; 149 struct cnode *ptrprev = NULL; 150 151 ptr = coda_cache[coda_hash(&cp->c_fid)]; 152 while (ptr != NULL) { 153 if (ptr == cp) { 154 if (ptrprev == NULL) { 155 coda_cache[coda_hash(&cp->c_fid)] 156 = CNODE_NEXT(ptr); 157 } else { 158 CNODE_NEXT(ptrprev) = CNODE_NEXT(ptr); 159 } 160 CNODE_NEXT(cp) = (struct cnode *)NULL; 161 162 return; 163 } 164 ptrprev = ptr; 165 ptr = CNODE_NEXT(ptr); 166 } 167 } 168 169 /* 170 * Lookup a cnode by fid. If the cnode is dying, it is bogus so skip it. 171 * NOTE: this allows multiple cnodes with same fid -- dcs 1/25/95 172 */ 173 struct cnode * 174 coda_find(CodaFid *fid) 175 { 176 struct cnode *cp; 177 178 cp = coda_cache[coda_hash(fid)]; 179 while (cp) { 180 if (coda_fid_eq(&(cp->c_fid), fid) && 181 (!IS_UNMOUNTING(cp))) 182 { 183 coda_active++; 184 return(cp); 185 } 186 cp = CNODE_NEXT(cp); 187 } 188 return(NULL); 189 } 190 191 /* 192 * coda_kill is called as a side effect to vcopen. To prevent any 193 * cnodes left around from an earlier run of a venus or warden from 194 * causing problems with the new instance, mark any outstanding cnodes 195 * as dying. Future operations on these cnodes should fail (excepting 196 * coda_inactive of course!). Since multiple venii/wardens can be 197 * running, only kill the cnodes for a particular entry in the 198 * coda_mnttbl. -- DCS 12/1/94 */ 199 200 int 201 coda_kill(struct mount *whoIam, enum dc_status dcstat) 202 { 203 int hash, count = 0; 204 struct cnode *cp; 205 206 /* 207 * Algorithm is as follows: 208 * Second, flush whatever vnodes we can from the name cache. 209 * 210 * Finally, step through whatever is left and mark them dying. 211 * This prevents any operation at all. 212 213 */ 214 215 /* This is slightly overkill, but should work. Eventually it'd be 216 * nice to only flush those entries from the namecache that 217 * reference a vnode in this vfs. */ 218 coda_nc_flush(dcstat); 219 220 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 221 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 222 if (CTOV(cp)->v_mount == whoIam) { 223 #ifdef DEBUG 224 printf("coda_kill: vp %p, cp %p\n", CTOV(cp), cp); 225 #endif 226 count++; 227 CODADEBUG(CODA_FLUSH, 228 myprintf(("Live cnode fid %s flags %d count %d\n", 229 coda_f2s(&cp->c_fid), 230 cp->c_flags, 231 CTOV(cp)->v_usecount)); ); 232 } 233 } 234 } 235 return count; 236 } 237 238 /* 239 * There are two reasons why a cnode may be in use, it may be in the 240 * name cache or it may be executing. 241 */ 242 void 243 coda_flush(enum dc_status dcstat) 244 { 245 int hash; 246 struct cnode *cp; 247 248 coda_clstat.ncalls++; 249 coda_clstat.reqs[CODA_FLUSH]++; 250 251 coda_nc_flush(dcstat); /* flush files from the name cache */ 252 253 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 254 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 255 if (!IS_DIR(cp->c_fid)) /* only files can be executed */ 256 coda_vmflush(cp); 257 } 258 } 259 } 260 261 /* 262 * As a debugging measure, print out any cnodes that lived through a 263 * name cache flush. 264 */ 265 void 266 coda_testflush(void) 267 { 268 int hash; 269 struct cnode *cp; 270 271 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 272 for (cp = coda_cache[hash]; 273 cp != NULL; 274 cp = CNODE_NEXT(cp)) { 275 myprintf(("Live cnode fid %s count %d\n", 276 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount)); 277 } 278 } 279 } 280 281 /* 282 * First, step through all cnodes and mark them unmounting. 283 * NetBSD kernels may try to fsync them now that venus 284 * is dead, which would be a bad thing. 285 * 286 */ 287 void 288 coda_unmounting(struct mount *whoIam) 289 { 290 int hash; 291 struct cnode *cp; 292 293 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 294 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 295 if (CTOV(cp)->v_mount == whoIam) { 296 if (cp->c_flags & (C_LOCKED|C_WANTED)) { 297 printf("coda_unmounting: Unlocking %p\n", cp); 298 cp->c_flags &= ~(C_LOCKED|C_WANTED); 299 wakeup((caddr_t) cp); 300 } 301 cp->c_flags |= C_UNMOUNTING; 302 } 303 } 304 } 305 } 306 307 #ifdef DEBUG 308 void 309 coda_checkunmounting(struct mount *mp) 310 { 311 struct vnode *vp, *nvp; 312 struct cnode *cp; 313 int count = 0, bad = 0; 314 loop: 315 for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) { 316 if (vp->v_mount != mp) 317 goto loop; 318 nvp = vp->v_mntvnodes.le_next; 319 cp = VTOC(vp); 320 count++; 321 if (!(cp->c_flags & C_UNMOUNTING)) { 322 bad++; 323 printf("vp %p, cp %p missed\n", vp, cp); 324 cp->c_flags |= C_UNMOUNTING; 325 } 326 } 327 } 328 329 void 330 coda_cacheprint(struct mount *whoIam) 331 { 332 int hash; 333 struct cnode *cp; 334 int count = 0; 335 336 printf("coda_cacheprint: coda_ctlvp %p, cp %p", coda_ctlvp, VTOC(coda_ctlvp)); 337 coda_nc_name(VTOC(coda_ctlvp)); 338 printf("\n"); 339 340 for (hash = 0; hash < CODA_CACHESIZE; hash++) { 341 for (cp = coda_cache[hash]; cp != NULL; cp = CNODE_NEXT(cp)) { 342 if (CTOV(cp)->v_mount == whoIam) { 343 printf("coda_cacheprint: vp %p, cp %p", CTOV(cp), cp); 344 coda_nc_name(cp); 345 printf("\n"); 346 count++; 347 } 348 } 349 } 350 printf("coda_cacheprint: count %d\n", count); 351 } 352 #endif 353 354 /* 355 * There are 6 cases where invalidations occur. The semantics of each 356 * is listed here. 357 * 358 * CODA_FLUSH -- flush all entries from the name cache and the cnode cache. 359 * CODA_PURGEUSER -- flush all entries from the name cache for a specific user 360 * This call is a result of token expiration. 361 * 362 * The next two are the result of callbacks on a file or directory. 363 * CODA_ZAPDIR -- flush the attributes for the dir from its cnode. 364 * Zap all children of this directory from the namecache. 365 * CODA_ZAPFILE -- flush the attributes for a file. 366 * 367 * The fifth is a result of Venus detecting an inconsistent file. 368 * CODA_PURGEFID -- flush the attribute for the file 369 * If it is a dir (odd vnode), purge its 370 * children from the namecache 371 * remove the file from the namecache. 372 * 373 * The sixth allows Venus to replace local fids with global ones 374 * during reintegration. 375 * 376 * CODA_REPLACE -- replace one CodaFid with another throughout the name cache 377 */ 378 379 int handleDownCall(int opcode, union outputArgs *out) 380 { 381 int error; 382 383 /* Handle invalidate requests. */ 384 switch (opcode) { 385 case CODA_FLUSH : { 386 387 coda_flush(IS_DOWNCALL); 388 389 CODADEBUG(CODA_FLUSH,coda_testflush();) /* print remaining cnodes */ 390 return(0); 391 } 392 393 case CODA_PURGEUSER : { 394 coda_clstat.ncalls++; 395 coda_clstat.reqs[CODA_PURGEUSER]++; 396 397 /* XXX - need to prevent fsync's */ 398 #ifdef CODA_COMPAT_5 399 coda_nc_purge_user(out->coda_purgeuser.cred.cr_uid, IS_DOWNCALL); 400 #else 401 coda_nc_purge_user(out->coda_purgeuser.uid, IS_DOWNCALL); 402 #endif 403 return(0); 404 } 405 406 case CODA_ZAPFILE : { 407 struct cnode *cp; 408 409 error = 0; 410 coda_clstat.ncalls++; 411 coda_clstat.reqs[CODA_ZAPFILE]++; 412 413 cp = coda_find(&out->coda_zapfile.Fid); 414 if (cp != NULL) { 415 vref(CTOV(cp)); 416 417 cp->c_flags &= ~C_VATTR; 418 if (CTOV(cp)->v_flag & VTEXT) 419 error = coda_vmflush(cp); 420 CODADEBUG(CODA_ZAPFILE, myprintf(( 421 "zapfile: fid = %s, refcnt = %d, error = %d\n", 422 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount - 1, error));); 423 if (CTOV(cp)->v_usecount == 1) { 424 cp->c_flags |= C_PURGING; 425 } 426 vrele(CTOV(cp)); 427 } 428 429 return(error); 430 } 431 432 case CODA_ZAPDIR : { 433 struct cnode *cp; 434 435 coda_clstat.ncalls++; 436 coda_clstat.reqs[CODA_ZAPDIR]++; 437 438 cp = coda_find(&out->coda_zapdir.Fid); 439 if (cp != NULL) { 440 vref(CTOV(cp)); 441 442 cp->c_flags &= ~C_VATTR; 443 coda_nc_zapParentfid(&out->coda_zapdir.Fid, IS_DOWNCALL); 444 445 CODADEBUG(CODA_ZAPDIR, myprintf(( 446 "zapdir: fid = %s, refcnt = %d\n", 447 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount - 1));); 448 if (CTOV(cp)->v_usecount == 1) { 449 cp->c_flags |= C_PURGING; 450 } 451 vrele(CTOV(cp)); 452 } 453 454 return(0); 455 } 456 457 case CODA_PURGEFID : { 458 struct cnode *cp; 459 460 error = 0; 461 coda_clstat.ncalls++; 462 coda_clstat.reqs[CODA_PURGEFID]++; 463 464 cp = coda_find(&out->coda_purgefid.Fid); 465 if (cp != NULL) { 466 vref(CTOV(cp)); 467 if (IS_DIR(out->coda_purgefid.Fid)) { /* Vnode is a directory */ 468 coda_nc_zapParentfid(&out->coda_purgefid.Fid, 469 IS_DOWNCALL); 470 } 471 cp->c_flags &= ~C_VATTR; 472 coda_nc_zapfid(&out->coda_purgefid.Fid, IS_DOWNCALL); 473 if (!(IS_DIR(out->coda_purgefid.Fid)) 474 && (CTOV(cp)->v_flag & VTEXT)) { 475 476 error = coda_vmflush(cp); 477 } 478 CODADEBUG(CODA_PURGEFID, myprintf(( 479 "purgefid: fid = %s, refcnt = %d, error = %d\n", 480 coda_f2s(&cp->c_fid), CTOV(cp)->v_usecount - 1, error));); 481 if (CTOV(cp)->v_usecount == 1) { 482 cp->c_flags |= C_PURGING; 483 } 484 vrele(CTOV(cp)); 485 } 486 return(error); 487 } 488 489 case CODA_REPLACE : { 490 struct cnode *cp = NULL; 491 492 coda_clstat.ncalls++; 493 coda_clstat.reqs[CODA_REPLACE]++; 494 495 cp = coda_find(&out->coda_replace.OldFid); 496 if (cp != NULL) { 497 /* remove the cnode from the hash table, replace the fid, and reinsert */ 498 vref(CTOV(cp)); 499 coda_unsave(cp); 500 cp->c_fid = out->coda_replace.NewFid; 501 coda_save(cp); 502 503 CODADEBUG(CODA_REPLACE, myprintf(( 504 "replace: oldfid = %s, newfid = %s, cp = %p\n", 505 coda_f2s(&out->coda_replace.OldFid), 506 coda_f2s(&cp->c_fid), cp));) 507 vrele(CTOV(cp)); 508 } 509 return (0); 510 } 511 default: 512 myprintf(("handleDownCall: unknown opcode %d\n", opcode)); 513 return (EINVAL); 514 } 515 } 516 517 /* coda_grab_vnode: lives in either cfs_mach.c or cfs_nbsd.c */ 518 519 int 520 coda_vmflush(struct cnode *cp) 521 { 522 return 0; 523 } 524 525 526 /* 527 * kernel-internal debugging switches 528 */ 529 530 void coda_debugon(void) 531 { 532 codadebug = -1; 533 coda_nc_debug = -1; 534 coda_vnop_print_entry = 1; 535 coda_psdev_print_entry = 1; 536 coda_vfsop_print_entry = 1; 537 } 538 539 void coda_debugoff(void) 540 { 541 codadebug = 0; 542 coda_nc_debug = 0; 543 coda_vnop_print_entry = 0; 544 coda_psdev_print_entry = 0; 545 coda_vfsop_print_entry = 0; 546 } 547 548 /* 549 * Utilities used by both client and server 550 * Standard levels: 551 * 0) no debugging 552 * 1) hard failures 553 * 2) soft failures 554 * 3) current test software 555 * 4) main procedure entry points 556 * 5) main procedure exit points 557 * 6) utility procedure entry points 558 * 7) utility procedure exit points 559 * 8) obscure procedure entry points 560 * 9) obscure procedure exit points 561 * 10) random stuff 562 * 11) all <= 1 563 * 12) all <= 2 564 * 13) all <= 3 565 * ... 566 */ 567