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