coda_vfsops.c revision 120011
1/* 2 * Coda: an Experimental Distributed File System 3 * Release 3.1 4 * 5 * Copyright (c) 1987-1998 Carnegie Mellon University 6 * All Rights Reserved 7 * 8 * Permission to use, copy, modify and distribute this software and its 9 * documentation is hereby granted, provided that both the copyright 10 * notice and this permission notice appear in all copies of the 11 * software, derivative works or modified versions, and any portions 12 * thereof, and that both notices appear in supporting documentation, and 13 * that credit is given to Carnegie Mellon University in all documents 14 * and publicity pertaining to direct or indirect use of this code or its 15 * derivatives. 16 * 17 * CODA IS AN EXPERIMENTAL SOFTWARE SYSTEM AND IS KNOWN TO HAVE BUGS, 18 * SOME OF WHICH MAY HAVE SERIOUS CONSEQUENCES. CARNEGIE MELLON ALLOWS 19 * FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION. CARNEGIE MELLON 20 * DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER 21 * RESULTING DIRECTLY OR INDIRECTLY FROM THE USE OF THIS SOFTWARE OR OF 22 * ANY DERIVATIVE WORK. 23 * 24 * Carnegie Mellon encourages users of this software to return any 25 * improvements or extensions that they make, and to grant Carnegie 26 * Mellon the rights to redistribute these changes without encumbrance. 27 * 28 * @(#) src/sys/cfs/coda_vfsops.c,v 1.1.1.1 1998/08/29 21:14:52 rvb Exp $ 29 */ 30/* 31 * Mach Operating System 32 * Copyright (c) 1989 Carnegie-Mellon University 33 * All rights reserved. The CMU software License Agreement specifies 34 * the terms and conditions for use and redistribution. 35 */ 36 37/* 38 * This code was written for the Coda filesystem at Carnegie Mellon 39 * University. Contributers include David Steere, James Kistler, and 40 * M. Satyanarayanan. 41 */ 42 43#include <sys/cdefs.h> 44__FBSDID("$FreeBSD: head/sys/fs/coda/coda_vfsops.c 120011 2003-09-13 01:13:56Z tjr $"); 45 46#include <vcoda.h> 47 48#include <sys/param.h> 49#include <sys/systm.h> 50#include <sys/conf.h> 51#include <sys/kernel.h> 52#include <sys/lock.h> 53#include <sys/malloc.h> 54#include <sys/mount.h> 55#include <sys/namei.h> 56#include <sys/proc.h> 57 58#include <coda/coda.h> 59#include <coda/cnode.h> 60#include <coda/coda_vfsops.h> 61#include <coda/coda_venus.h> 62#include <coda/coda_subr.h> 63#include <coda/coda_opstats.h> 64 65MALLOC_DEFINE(M_CODA, "CODA storage", "Various Coda Structures"); 66 67int codadebug = 0; 68int coda_vfsop_print_entry = 0; 69#define ENTRY if(coda_vfsop_print_entry) myprintf(("Entered %s\n",__func__)) 70 71struct vnode *coda_ctlvp; 72struct coda_mntinfo coda_mnttbl[NVCODA]; /* indexed by minor device number */ 73 74/* structure to keep statistics of internally generated/satisfied calls */ 75 76struct coda_op_stats coda_vfsopstats[CODA_VFSOPS_SIZE]; 77 78#define MARK_ENTRY(op) (coda_vfsopstats[op].entries++) 79#define MARK_INT_SAT(op) (coda_vfsopstats[op].sat_intrn++) 80#define MARK_INT_FAIL(op) (coda_vfsopstats[op].unsat_intrn++) 81#define MRAK_INT_GEN(op) (coda_vfsopstats[op].gen_intrn++) 82 83extern int coda_nc_initialized; /* Set if cache has been initialized */ 84extern int vc_nb_open(dev_t, int, int, struct thread *); 85 86int 87coda_vfsopstats_init(void) 88{ 89 register int i; 90 91 for (i=0;i<CODA_VFSOPS_SIZE;i++) { 92 coda_vfsopstats[i].opcode = i; 93 coda_vfsopstats[i].entries = 0; 94 coda_vfsopstats[i].sat_intrn = 0; 95 coda_vfsopstats[i].unsat_intrn = 0; 96 coda_vfsopstats[i].gen_intrn = 0; 97 } 98 99 return 0; 100} 101 102/* 103 * cfs mount vfsop 104 * Set up mount info record and attach it to vfs struct. 105 */ 106/*ARGSUSED*/ 107int 108coda_mount(vfsp, path, data, ndp, td) 109 struct mount *vfsp; /* Allocated and initialized by mount(2) */ 110 char *path; /* path covered: ignored by the fs-layer */ 111 caddr_t data; /* Need to define a data type for this in netbsd? */ 112 struct nameidata *ndp; /* Clobber this to lookup the device name */ 113 struct thread *td; 114{ 115 struct vnode *dvp; 116 struct cnode *cp; 117 dev_t dev; 118 struct coda_mntinfo *mi; 119 struct vnode *rootvp; 120 CodaFid rootfid = INVAL_FID; 121 CodaFid ctlfid = CTL_FID; 122 int error; 123 124 ENTRY; 125 126 coda_vfsopstats_init(); 127 coda_vnodeopstats_init(); 128 129 MARK_ENTRY(CODA_MOUNT_STATS); 130 if (CODA_MOUNTED(vfsp)) { 131 MARK_INT_FAIL(CODA_MOUNT_STATS); 132 return(EBUSY); 133 } 134 135 /* Validate mount device. Similar to getmdev(). */ 136 NDINIT(ndp, LOOKUP, FOLLOW, UIO_USERSPACE, data, td); 137 error = namei(ndp); 138 dvp = ndp->ni_vp; 139 140 if (error) { 141 MARK_INT_FAIL(CODA_MOUNT_STATS); 142 return (error); 143 } 144 if (dvp->v_type != VCHR) { 145 MARK_INT_FAIL(CODA_MOUNT_STATS); 146 vrele(dvp); 147 NDFREE(ndp, NDF_ONLY_PNBUF); 148 return(ENXIO); 149 } 150 dev = dvp->v_rdev; 151 vrele(dvp); 152 NDFREE(ndp, NDF_ONLY_PNBUF); 153 154 /* 155 * See if the device table matches our expectations. 156 */ 157 if (devsw(dev)->d_open != vc_nb_open) 158 { 159 MARK_INT_FAIL(CODA_MOUNT_STATS); 160 return(ENXIO); 161 } 162 163 if (minor(dev) >= NVCODA || minor(dev) < 0) { 164 MARK_INT_FAIL(CODA_MOUNT_STATS); 165 return(ENXIO); 166 } 167 168 /* 169 * Initialize the mount record and link it to the vfs struct 170 */ 171 mi = &coda_mnttbl[minor(dev)]; 172 173 if (!VC_OPEN(&mi->mi_vcomm)) { 174 MARK_INT_FAIL(CODA_MOUNT_STATS); 175 return(ENODEV); 176 } 177 178 /* No initialization (here) of mi_vcomm! */ 179 vfsp->mnt_data = (qaddr_t)mi; 180 vfs_getnewfsid (vfsp); 181 182 mi->mi_vfsp = vfsp; 183 mi->mi_started = 0; /* XXX See coda_root() */ 184 185 /* 186 * Make a root vnode to placate the Vnode interface, but don't 187 * actually make the CODA_ROOT call to venus until the first call 188 * to coda_root in case a server is down while venus is starting. 189 */ 190 cp = make_coda_node(&rootfid, vfsp, VDIR); 191 rootvp = CTOV(cp); 192 rootvp->v_vflag |= VV_ROOT; 193 194/* cp = make_coda_node(&ctlfid, vfsp, VCHR); 195 The above code seems to cause a loop in the cnode links. 196 I don't totally understand when it happens, it is caught 197 when closing down the system. 198 */ 199 cp = make_coda_node(&ctlfid, 0, VCHR); 200 201 coda_ctlvp = CTOV(cp); 202 203 /* Add vfs and rootvp to chain of vfs hanging off mntinfo */ 204 mi->mi_vfsp = vfsp; 205 mi->mi_rootvp = rootvp; 206 207 /* set filesystem block size */ 208 vfsp->mnt_stat.f_bsize = 8192; /* XXX -JJK */ 209 210 /* Set f_iosize. XXX -- inamura@isl.ntt.co.jp. 211 For vnode_pager_haspage() references. The value should be obtained 212 from underlying UFS. */ 213 /* Checked UFS. iosize is set as 8192 */ 214 vfsp->mnt_stat.f_iosize = 8192; 215 216 /* error is currently guaranteed to be zero, but in case some 217 code changes... */ 218 CODADEBUG(1, 219 myprintf(("coda_mount returned %d\n",error));); 220 if (error) 221 MARK_INT_FAIL(CODA_MOUNT_STATS); 222 else 223 MARK_INT_SAT(CODA_MOUNT_STATS); 224 225 return(error); 226} 227 228int 229coda_unmount(vfsp, mntflags, td) 230 struct mount *vfsp; 231 int mntflags; 232 struct thread *td; 233{ 234 struct coda_mntinfo *mi = vftomi(vfsp); 235 int active, error = 0; 236 237 ENTRY; 238 MARK_ENTRY(CODA_UMOUNT_STATS); 239 if (!CODA_MOUNTED(vfsp)) { 240 MARK_INT_FAIL(CODA_UMOUNT_STATS); 241 return(EINVAL); 242 } 243 244 if (mi->mi_vfsp == vfsp) { /* We found the victim */ 245 if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp))) 246 return (EBUSY); /* Venus is still running */ 247 248#ifdef DEBUG 249 printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp, VTOC(mi->mi_rootvp)); 250#endif 251 vrele(mi->mi_rootvp); 252 active = coda_kill(vfsp, NOT_DOWNCALL); 253 ASSERT_VOP_LOCKED(mi->mi_rootvp, "coda_unmount"); 254 mi->mi_rootvp->v_vflag &= ~VV_ROOT; 255 error = vflush(mi->mi_vfsp, 0, FORCECLOSE); 256#ifdef CODA_VERBOSE 257 printf("coda_unmount: active = %d, vflush active %d\n", active, error); 258#endif 259 error = 0; 260 /* I'm going to take this out to allow lookups to go through. I'm 261 * not sure it's important anyway. -- DCS 2/2/94 262 */ 263 /* vfsp->VFS_DATA = NULL; */ 264 265 /* No more vfsp's to hold onto */ 266 mi->mi_vfsp = NULL; 267 mi->mi_rootvp = NULL; 268 269 if (error) 270 MARK_INT_FAIL(CODA_UMOUNT_STATS); 271 else 272 MARK_INT_SAT(CODA_UMOUNT_STATS); 273 274 return(error); 275 } 276 return (EINVAL); 277} 278 279/* 280 * find root of cfs 281 */ 282int 283coda_root(vfsp, vpp) 284 struct mount *vfsp; 285 struct vnode **vpp; 286{ 287 struct coda_mntinfo *mi = vftomi(vfsp); 288 struct vnode **result; 289 int error; 290 struct thread *td = curthread; /* XXX - bnoble */ 291 struct proc *p = td->td_proc; 292 CodaFid VFid; 293 static const CodaFid invalfid = INVAL_FID; 294 295 ENTRY; 296 MARK_ENTRY(CODA_ROOT_STATS); 297 result = NULL; 298 299 if (vfsp == mi->mi_vfsp) { 300 /* 301 * Cache the root across calls. We only need to pass the request 302 * on to Venus if the root vnode is the dummy we installed in 303 * coda_mount() with all c_fid members zeroed. 304 * 305 * XXX In addition, if we are called between coda_mount() and 306 * coda_start(), we assume that the request is from vfs_mount() 307 * (before the call to checkdirs()) and return the dummy root 308 * node to avoid a deadlock. This bug is fixed in the Coda CVS 309 * repository but not in any released versions as of 6 Mar 2003. 310 */ 311 if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid, 312 sizeof(CodaFid)) != 0 || mi->mi_started == 0) 313 { /* Found valid root. */ 314 *vpp = mi->mi_rootvp; 315 /* On Mach, this is vref. On NetBSD, VOP_LOCK */ 316#if 1 317 vref(*vpp); 318 vn_lock(*vpp, LK_EXCLUSIVE, td); 319#else 320 vget(*vpp, LK_EXCLUSIVE, td); 321#endif 322 MARK_INT_SAT(CODA_ROOT_STATS); 323 return(0); 324 } 325 } 326 327 error = venus_root(vftomi(vfsp), td->td_ucred, p, &VFid); 328 329 if (!error) { 330 /* 331 * Save the new rootfid in the cnode, and rehash the cnode into the 332 * cnode hash with the new fid key. 333 */ 334 coda_unsave(VTOC(mi->mi_rootvp)); 335 VTOC(mi->mi_rootvp)->c_fid = VFid; 336 coda_save(VTOC(mi->mi_rootvp)); 337 338 *vpp = mi->mi_rootvp; 339#if 1 340 vref(*vpp); 341 vn_lock(*vpp, LK_EXCLUSIVE, td); 342#else 343 vget(*vpp, LK_EXCLUSIVE, td); 344#endif 345 346 MARK_INT_SAT(CODA_ROOT_STATS); 347 goto exit; 348 } else if (error == ENODEV || error == EINTR) { 349 /* Gross hack here! */ 350 /* 351 * If Venus fails to respond to the CODA_ROOT call, coda_call returns 352 * ENODEV. Return the uninitialized root vnode to allow vfs 353 * operations such as unmount to continue. Without this hack, 354 * there is no way to do an unmount if Venus dies before a 355 * successful CODA_ROOT call is done. All vnode operations 356 * will fail. 357 */ 358 *vpp = mi->mi_rootvp; 359#if 1 360 vref(*vpp); 361 vn_lock(*vpp, LK_EXCLUSIVE, td); 362#else 363 vget(*vpp, LK_EXCLUSIVE, td); 364#endif 365 366 MARK_INT_FAIL(CODA_ROOT_STATS); 367 error = 0; 368 goto exit; 369 } else { 370 CODADEBUG( CODA_ROOT, myprintf(("error %d in CODA_ROOT\n", error)); ); 371 MARK_INT_FAIL(CODA_ROOT_STATS); 372 373 goto exit; 374 } 375 376 exit: 377 return(error); 378} 379 380int 381coda_start(mp, flags, td) 382 struct mount *mp; 383 int flags; 384 struct thread *td; 385{ 386 387 /* XXX See coda_root(). */ 388 vftomi(mp)->mi_started = 1; 389 return (0); 390} 391 392/* 393 * Get filesystem statistics. 394 */ 395int 396coda_nb_statfs(vfsp, sbp, td) 397 register struct mount *vfsp; 398 struct statfs *sbp; 399 struct thread *td; 400{ 401 ENTRY; 402/* MARK_ENTRY(CODA_STATFS_STATS); */ 403 if (!CODA_MOUNTED(vfsp)) { 404/* MARK_INT_FAIL(CODA_STATFS_STATS);*/ 405 return(EINVAL); 406 } 407 408 bzero(sbp, sizeof(struct statfs)); 409 /* XXX - what to do about f_flags, others? --bnoble */ 410 /* Below This is what AFS does 411 #define NB_SFS_SIZ 0x895440 412 */ 413 /* Note: Normal fs's have a bsize of 0x400 == 1024 */ 414 sbp->f_type = vfsp->mnt_vfc->vfc_typenum; 415 sbp->f_bsize = 8192; /* XXX */ 416 sbp->f_iosize = 8192; /* XXX */ 417#define NB_SFS_SIZ 0x8AB75D 418 sbp->f_blocks = NB_SFS_SIZ; 419 sbp->f_bfree = NB_SFS_SIZ; 420 sbp->f_bavail = NB_SFS_SIZ; 421 sbp->f_files = NB_SFS_SIZ; 422 sbp->f_ffree = NB_SFS_SIZ; 423 bcopy((caddr_t)&(vfsp->mnt_stat.f_fsid), (caddr_t)&(sbp->f_fsid), sizeof (fsid_t)); 424 snprintf(sbp->f_mntonname, sizeof(sbp->f_mntonname), "/coda"); 425 snprintf(sbp->f_mntfromname, sizeof(sbp->f_mntfromname), "CODA"); 426 snprintf(sbp->f_fstypename, sizeof(sbp->f_fstypename), "coda"); 427/* MARK_INT_SAT(CODA_STATFS_STATS); */ 428 return(0); 429} 430 431/* 432 * Flush any pending I/O. 433 */ 434int 435coda_sync(vfsp, waitfor, cred, td) 436 struct mount *vfsp; 437 int waitfor; 438 struct ucred *cred; 439 struct thread *td; 440{ 441 ENTRY; 442 MARK_ENTRY(CODA_SYNC_STATS); 443 MARK_INT_SAT(CODA_SYNC_STATS); 444 return(0); 445} 446 447/* 448 * fhtovp is now what vget used to be in 4.3-derived systems. For 449 * some silly reason, vget is now keyed by a 32 bit ino_t, rather than 450 * a type-specific fid. 451 */ 452int 453coda_fhtovp(vfsp, fhp, nam, vpp, exflagsp, creadanonp) 454 register struct mount *vfsp; 455 struct fid *fhp; 456 struct mbuf *nam; 457 struct vnode **vpp; 458 int *exflagsp; 459 struct ucred **creadanonp; 460{ 461 struct cfid *cfid = (struct cfid *)fhp; 462 struct cnode *cp = 0; 463 int error; 464 struct thread *td = curthread; /* XXX -mach */ 465 struct proc *p = td->td_proc; 466 CodaFid VFid; 467 int vtype; 468 469 ENTRY; 470 471 MARK_ENTRY(CODA_VGET_STATS); 472 /* Check for vget of control object. */ 473 if (IS_CTL_FID(&cfid->cfid_fid)) { 474 *vpp = coda_ctlvp; 475 vref(coda_ctlvp); 476 MARK_INT_SAT(CODA_VGET_STATS); 477 return(0); 478 } 479 480 error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, td->td_ucred, p, &VFid, &vtype); 481 482 if (error) { 483 CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error));) 484 *vpp = (struct vnode *)0; 485 } else { 486 CODADEBUG(CODA_VGET, 487 myprintf(("vget: %s type %d result %d\n", 488 coda_f2s(&VFid), vtype, error)); ) 489 cp = make_coda_node(&VFid, vfsp, vtype); 490 *vpp = CTOV(cp); 491 } 492 return(error); 493} 494 495/* 496 * To allow for greater ease of use, some vnodes may be orphaned when 497 * Venus dies. Certain operations should still be allowed to go 498 * through, but without propagating ophan-ness. So this function will 499 * get a new vnode for the file from the current run of Venus. */ 500 501int 502getNewVnode(vpp) 503 struct vnode **vpp; 504{ 505 struct cfid cfid; 506 struct coda_mntinfo *mi = vftomi((*vpp)->v_mount); 507 508 ENTRY; 509 510 cfid.cfid_len = (short)sizeof(CodaFid); 511 cfid.cfid_fid = VTOC(*vpp)->c_fid; /* Structure assignment. */ 512 /* XXX ? */ 513 514 /* We're guessing that if set, the 1st element on the list is a 515 * valid vnode to use. If not, return ENODEV as venus is dead. 516 */ 517 if (mi->mi_vfsp == NULL) 518 return ENODEV; 519 520 return coda_fhtovp(mi->mi_vfsp, (struct fid*)&cfid, NULL, vpp, 521 NULL, NULL); 522} 523 524#include <ufs/ufs/extattr.h> 525#include <ufs/ufs/quota.h> 526#include <ufs/ufs/ufsmount.h> 527/* get the mount structure corresponding to a given device. Assume 528 * device corresponds to a UFS. Return NULL if no device is found. 529 */ 530struct mount *devtomp(dev) 531 dev_t dev; 532{ 533 struct mount *mp; 534 535 TAILQ_FOREACH(mp, &mountlist, mnt_list) { 536 if (((VFSTOUFS(mp))->um_dev == dev)) { 537 /* mount corresponds to UFS and the device matches one we want */ 538 return(mp); 539 } 540 } 541 /* mount structure wasn't found */ 542 return(NULL); 543} 544 545struct vfsops coda_vfsops = { 546 .vfs_mount = coda_mount, 547 .vfs_root = coda_root, 548 .vfs_start = coda_start, 549 .vfs_statfs = coda_nb_statfs, 550 .vfs_sync = coda_sync, 551 .vfs_unmount = coda_unmount, 552}; 553 554VFS_SET(coda_vfsops, coda, VFCF_NETWORK); 555