coda_vfsops.c revision 132902
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 132902 2004-07-30 22:08:52Z phk $"); 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(struct cdev *, int, int, struct thread *); 85 86static vfs_omount_t coda_omount; 87 88int 89coda_vfsopstats_init(void) 90{ 91 register int i; 92 93 for (i=0;i<CODA_VFSOPS_SIZE;i++) { 94 coda_vfsopstats[i].opcode = i; 95 coda_vfsopstats[i].entries = 0; 96 coda_vfsopstats[i].sat_intrn = 0; 97 coda_vfsopstats[i].unsat_intrn = 0; 98 coda_vfsopstats[i].gen_intrn = 0; 99 } 100 101 return 0; 102} 103 104/* 105 * cfs mount vfsop 106 * Set up mount info record and attach it to vfs struct. 107 */ 108/*ARGSUSED*/ 109int 110coda_omount(vfsp, path, data, td) 111 struct mount *vfsp; /* Allocated and initialized by mount(2) */ 112 char *path; /* path covered: ignored by the fs-layer */ 113 caddr_t data; /* Need to define a data type for this in netbsd? */ 114 struct thread *td; 115{ 116 struct vnode *dvp; 117 struct cnode *cp; 118 struct cdev *dev; 119 struct coda_mntinfo *mi; 120 struct vnode *rootvp; 121 CodaFid rootfid = INVAL_FID; 122 CodaFid ctlfid = CTL_FID; 123 int error; 124 struct nameidata ndp; 125 ENTRY; 126 127 coda_vfsopstats_init(); 128 coda_vnodeopstats_init(); 129 130 MARK_ENTRY(CODA_MOUNT_STATS); 131 if (CODA_MOUNTED(vfsp)) { 132 MARK_INT_FAIL(CODA_MOUNT_STATS); 133 return(EBUSY); 134 } 135 136 /* Validate mount device. Similar to getmdev(). */ 137 NDINIT(&ndp, LOOKUP, FOLLOW, UIO_USERSPACE, data, td); 138 error = namei(&ndp); 139 dvp = ndp.ni_vp; 140 141 if (error) { 142 MARK_INT_FAIL(CODA_MOUNT_STATS); 143 return (error); 144 } 145 if (dvp->v_type != VCHR) { 146 MARK_INT_FAIL(CODA_MOUNT_STATS); 147 vrele(dvp); 148 NDFREE(&ndp, NDF_ONLY_PNBUF); 149 return(ENXIO); 150 } 151 dev = dvp->v_rdev; 152 vrele(dvp); 153 NDFREE(&ndp, NDF_ONLY_PNBUF); 154 155 /* 156 * See if the device table matches our expectations. 157 */ 158 if (devsw(dev)->d_open != vc_nb_open) 159 { 160 MARK_INT_FAIL(CODA_MOUNT_STATS); 161 return(ENXIO); 162 } 163 164 if (minor(dev) >= NVCODA || minor(dev) < 0) { 165 MARK_INT_FAIL(CODA_MOUNT_STATS); 166 return(ENXIO); 167 } 168 169 /* 170 * Initialize the mount record and link it to the vfs struct 171 */ 172 mi = &coda_mnttbl[minor(dev)]; 173 174 if (!VC_OPEN(&mi->mi_vcomm)) { 175 MARK_INT_FAIL(CODA_MOUNT_STATS); 176 return(ENODEV); 177 } 178 179 /* No initialization (here) of mi_vcomm! */ 180 vfsp->mnt_data = (qaddr_t)mi; 181 vfs_getnewfsid (vfsp); 182 183 mi->mi_vfsp = vfsp; 184 mi->mi_started = 0; /* XXX See coda_root() */ 185 186 /* 187 * Make a root vnode to placate the Vnode interface, but don't 188 * actually make the CODA_ROOT call to venus until the first call 189 * to coda_root in case a server is down while venus is starting. 190 */ 191 cp = make_coda_node(&rootfid, vfsp, VDIR); 192 rootvp = CTOV(cp); 193 rootvp->v_vflag |= VV_ROOT; 194 195/* cp = make_coda_node(&ctlfid, vfsp, VCHR); 196 The above code seems to cause a loop in the cnode links. 197 I don't totally understand when it happens, it is caught 198 when closing down the system. 199 */ 200 cp = make_coda_node(&ctlfid, 0, VCHR); 201 202 coda_ctlvp = CTOV(cp); 203 204 /* Add vfs and rootvp to chain of vfs hanging off mntinfo */ 205 mi->mi_vfsp = vfsp; 206 mi->mi_rootvp = rootvp; 207 208 /* set filesystem block size */ 209 vfsp->mnt_stat.f_bsize = 8192; /* XXX -JJK */ 210 211 /* Set f_iosize. XXX -- inamura@isl.ntt.co.jp. 212 For vnode_pager_haspage() references. The value should be obtained 213 from underlying UFS. */ 214 /* Checked UFS. iosize is set as 8192 */ 215 vfsp->mnt_stat.f_iosize = 8192; 216 217 /* error is currently guaranteed to be zero, but in case some 218 code changes... */ 219 CODADEBUG(1, 220 myprintf(("coda_omount returned %d\n",error));); 221 if (error) 222 MARK_INT_FAIL(CODA_MOUNT_STATS); 223 else 224 MARK_INT_SAT(CODA_MOUNT_STATS); 225 226 return(error); 227} 228 229int 230coda_unmount(vfsp, mntflags, td) 231 struct mount *vfsp; 232 int mntflags; 233 struct thread *td; 234{ 235 struct coda_mntinfo *mi = vftomi(vfsp); 236 int active, error = 0; 237 238 ENTRY; 239 MARK_ENTRY(CODA_UMOUNT_STATS); 240 if (!CODA_MOUNTED(vfsp)) { 241 MARK_INT_FAIL(CODA_UMOUNT_STATS); 242 return(EINVAL); 243 } 244 245 if (mi->mi_vfsp == vfsp) { /* We found the victim */ 246 if (!IS_UNMOUNTING(VTOC(mi->mi_rootvp))) 247 return (EBUSY); /* Venus is still running */ 248 249#ifdef DEBUG 250 printf("coda_unmount: ROOT: vp %p, cp %p\n", mi->mi_rootvp, VTOC(mi->mi_rootvp)); 251#endif 252 vrele(mi->mi_rootvp); 253 active = coda_kill(vfsp, NOT_DOWNCALL); 254 ASSERT_VOP_LOCKED(mi->mi_rootvp, "coda_unmount"); 255 mi->mi_rootvp->v_vflag &= ~VV_ROOT; 256 error = vflush(mi->mi_vfsp, 0, FORCECLOSE, td); 257#ifdef CODA_VERBOSE 258 printf("coda_unmount: active = %d, vflush active %d\n", active, error); 259#endif 260 error = 0; 261 /* I'm going to take this out to allow lookups to go through. I'm 262 * not sure it's important anyway. -- DCS 2/2/94 263 */ 264 /* vfsp->VFS_DATA = NULL; */ 265 266 /* No more vfsp's to hold onto */ 267 mi->mi_vfsp = NULL; 268 mi->mi_rootvp = NULL; 269 270 if (error) 271 MARK_INT_FAIL(CODA_UMOUNT_STATS); 272 else 273 MARK_INT_SAT(CODA_UMOUNT_STATS); 274 275 return(error); 276 } 277 return (EINVAL); 278} 279 280/* 281 * find root of cfs 282 */ 283int 284coda_root(vfsp, vpp, td) 285 struct mount *vfsp; 286 struct vnode **vpp; 287 struct thread *td; 288{ 289 struct coda_mntinfo *mi = vftomi(vfsp); 290 struct vnode **result; 291 int error; 292 struct proc *p = td->td_proc; 293 CodaFid VFid; 294 static const CodaFid invalfid = INVAL_FID; 295 296 ENTRY; 297 MARK_ENTRY(CODA_ROOT_STATS); 298 result = NULL; 299 300 if (vfsp == mi->mi_vfsp) { 301 /* 302 * Cache the root across calls. We only need to pass the request 303 * on to Venus if the root vnode is the dummy we installed in 304 * coda_omount() with all c_fid members zeroed. 305 * 306 * XXX In addition, if we are called between coda_omount() and 307 * coda_start(), we assume that the request is from vfs_omount() 308 * (before the call to checkdirs()) and return the dummy root 309 * node to avoid a deadlock. This bug is fixed in the Coda CVS 310 * repository but not in any released versions as of 6 Mar 2003. 311 */ 312 if (memcmp(&VTOC(mi->mi_rootvp)->c_fid, &invalfid, 313 sizeof(CodaFid)) != 0 || mi->mi_started == 0) 314 { /* Found valid root. */ 315 *vpp = mi->mi_rootvp; 316 /* On Mach, this is vref. On NetBSD, VOP_LOCK */ 317#if 1 318 vref(*vpp); 319 vn_lock(*vpp, LK_EXCLUSIVE, td); 320#else 321 vget(*vpp, LK_EXCLUSIVE, td); 322#endif 323 MARK_INT_SAT(CODA_ROOT_STATS); 324 return(0); 325 } 326 } 327 328 error = venus_root(vftomi(vfsp), td->td_ucred, p, &VFid); 329 330 if (!error) { 331 /* 332 * Save the new rootfid in the cnode, and rehash the cnode into the 333 * cnode hash with the new fid key. 334 */ 335 coda_unsave(VTOC(mi->mi_rootvp)); 336 VTOC(mi->mi_rootvp)->c_fid = VFid; 337 coda_save(VTOC(mi->mi_rootvp)); 338 339 *vpp = mi->mi_rootvp; 340#if 1 341 vref(*vpp); 342 vn_lock(*vpp, LK_EXCLUSIVE, td); 343#else 344 vget(*vpp, LK_EXCLUSIVE, td); 345#endif 346 347 MARK_INT_SAT(CODA_ROOT_STATS); 348 goto exit; 349 } else if (error == ENODEV || error == EINTR) { 350 /* Gross hack here! */ 351 /* 352 * If Venus fails to respond to the CODA_ROOT call, coda_call returns 353 * ENODEV. Return the uninitialized root vnode to allow vfs 354 * operations such as unmount to continue. Without this hack, 355 * there is no way to do an unmount if Venus dies before a 356 * successful CODA_ROOT call is done. All vnode operations 357 * will fail. 358 */ 359 *vpp = mi->mi_rootvp; 360#if 1 361 vref(*vpp); 362 vn_lock(*vpp, LK_EXCLUSIVE, td); 363#else 364 vget(*vpp, LK_EXCLUSIVE, td); 365#endif 366 367 MARK_INT_FAIL(CODA_ROOT_STATS); 368 error = 0; 369 goto exit; 370 } else { 371 CODADEBUG( CODA_ROOT, myprintf(("error %d in CODA_ROOT\n", error)); ); 372 MARK_INT_FAIL(CODA_ROOT_STATS); 373 374 goto exit; 375 } 376 377 exit: 378 return(error); 379} 380 381int 382coda_start(mp, flags, td) 383 struct mount *mp; 384 int flags; 385 struct thread *td; 386{ 387 388 /* XXX See coda_root(). */ 389 vftomi(mp)->mi_started = 1; 390 return (0); 391} 392 393/* 394 * Get filesystem statistics. 395 */ 396int 397coda_nb_statfs(vfsp, sbp, td) 398 register struct mount *vfsp; 399 struct statfs *sbp; 400 struct thread *td; 401{ 402 ENTRY; 403/* MARK_ENTRY(CODA_STATFS_STATS); */ 404 if (!CODA_MOUNTED(vfsp)) { 405/* MARK_INT_FAIL(CODA_STATFS_STATS);*/ 406 return(EINVAL); 407 } 408 409 bzero(sbp, sizeof(struct statfs)); 410 /* XXX - what to do about f_flags, others? --bnoble */ 411 /* Below This is what AFS does 412 #define NB_SFS_SIZ 0x895440 413 */ 414 /* Note: Normal fs's have a bsize of 0x400 == 1024 */ 415 sbp->f_type = vfsp->mnt_vfc->vfc_typenum; 416 sbp->f_bsize = 8192; /* XXX */ 417 sbp->f_iosize = 8192; /* XXX */ 418#define NB_SFS_SIZ 0x8AB75D 419 sbp->f_blocks = NB_SFS_SIZ; 420 sbp->f_bfree = NB_SFS_SIZ; 421 sbp->f_bavail = NB_SFS_SIZ; 422 sbp->f_files = NB_SFS_SIZ; 423 sbp->f_ffree = NB_SFS_SIZ; 424 bcopy((caddr_t)&(vfsp->mnt_stat.f_fsid), (caddr_t)&(sbp->f_fsid), sizeof (fsid_t)); 425 snprintf(sbp->f_mntonname, sizeof(sbp->f_mntonname), "/coda"); 426 snprintf(sbp->f_mntfromname, sizeof(sbp->f_mntfromname), "CODA"); 427 snprintf(sbp->f_fstypename, sizeof(sbp->f_fstypename), "coda"); 428/* MARK_INT_SAT(CODA_STATFS_STATS); */ 429 return(0); 430} 431 432/* 433 * Flush any pending I/O. 434 */ 435int 436coda_sync(vfsp, waitfor, cred, td) 437 struct mount *vfsp; 438 int waitfor; 439 struct ucred *cred; 440 struct thread *td; 441{ 442 ENTRY; 443 MARK_ENTRY(CODA_SYNC_STATS); 444 MARK_INT_SAT(CODA_SYNC_STATS); 445 return(0); 446} 447 448/* 449 * fhtovp is now what vget used to be in 4.3-derived systems. For 450 * some silly reason, vget is now keyed by a 32 bit ino_t, rather than 451 * a type-specific fid. 452 */ 453int 454coda_fhtovp(vfsp, fhp, nam, vpp, exflagsp, creadanonp) 455 register struct mount *vfsp; 456 struct fid *fhp; 457 struct mbuf *nam; 458 struct vnode **vpp; 459 int *exflagsp; 460 struct ucred **creadanonp; 461{ 462 struct cfid *cfid = (struct cfid *)fhp; 463 struct cnode *cp = 0; 464 int error; 465 struct thread *td = curthread; /* XXX -mach */ 466 struct proc *p = td->td_proc; 467 CodaFid VFid; 468 int vtype; 469 470 ENTRY; 471 472 MARK_ENTRY(CODA_VGET_STATS); 473 /* Check for vget of control object. */ 474 if (IS_CTL_FID(&cfid->cfid_fid)) { 475 *vpp = coda_ctlvp; 476 vref(coda_ctlvp); 477 MARK_INT_SAT(CODA_VGET_STATS); 478 return(0); 479 } 480 481 error = venus_fhtovp(vftomi(vfsp), &cfid->cfid_fid, td->td_ucred, p, &VFid, &vtype); 482 483 if (error) { 484 CODADEBUG(CODA_VGET, myprintf(("vget error %d\n",error));) 485 *vpp = (struct vnode *)0; 486 } else { 487 CODADEBUG(CODA_VGET, 488 myprintf(("vget: %s type %d result %d\n", 489 coda_f2s(&VFid), vtype, error)); ) 490 cp = make_coda_node(&VFid, vfsp, vtype); 491 *vpp = CTOV(cp); 492 } 493 return(error); 494} 495 496/* 497 * To allow for greater ease of use, some vnodes may be orphaned when 498 * Venus dies. Certain operations should still be allowed to go 499 * through, but without propagating ophan-ness. So this function will 500 * get a new vnode for the file from the current run of Venus. */ 501 502int 503getNewVnode(vpp) 504 struct vnode **vpp; 505{ 506 struct cfid cfid; 507 struct coda_mntinfo *mi = vftomi((*vpp)->v_mount); 508 509 ENTRY; 510 511 cfid.cfid_len = (short)sizeof(CodaFid); 512 cfid.cfid_fid = VTOC(*vpp)->c_fid; /* Structure assignment. */ 513 /* XXX ? */ 514 515 /* We're guessing that if set, the 1st element on the list is a 516 * valid vnode to use. If not, return ENODEV as venus is dead. 517 */ 518 if (mi->mi_vfsp == NULL) 519 return ENODEV; 520 521 return coda_fhtovp(mi->mi_vfsp, (struct fid*)&cfid, NULL, vpp, 522 NULL, NULL); 523} 524 525#include <ufs/ufs/extattr.h> 526#include <ufs/ufs/quota.h> 527#include <ufs/ufs/ufsmount.h> 528/* get the mount structure corresponding to a given device. Assume 529 * device corresponds to a UFS. Return NULL if no device is found. 530 */ 531struct mount *devtomp(dev) 532 struct cdev *dev; 533{ 534 struct mount *mp; 535 536 TAILQ_FOREACH(mp, &mountlist, mnt_list) { 537 if (((VFSTOUFS(mp))->um_dev == dev)) { 538 /* mount corresponds to UFS and the device matches one we want */ 539 return(mp); 540 } 541 } 542 /* mount structure wasn't found */ 543 return(NULL); 544} 545 546struct vfsops coda_vfsops = { 547 .vfs_omount = coda_omount, 548 .vfs_root = coda_root, 549 .vfs_start = coda_start, 550 .vfs_statfs = coda_nb_statfs, 551 .vfs_sync = coda_sync, 552 .vfs_unmount = coda_unmount, 553}; 554 555VFS_SET(coda_vfsops, coda, VFCF_NETWORK); 556