137488Smckusick /* 237488Smckusick * Copyright (c) 1989 The Regents of the University of California. 337488Smckusick * All rights reserved. 437488Smckusick * 537488Smckusick * Redistribution and use in source and binary forms are permitted 637488Smckusick * provided that the above copyright notice and this paragraph are 737488Smckusick * duplicated in all such forms and that any documentation, 837488Smckusick * advertising materials, and other materials related to such 937488Smckusick * distribution and use acknowledge that the software was developed 1037488Smckusick * by the University of California, Berkeley. The name of the 1137488Smckusick * University may not be used to endorse or promote products derived 1237488Smckusick * from this software without specific prior written permission. 1337488Smckusick * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1437488Smckusick * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1537488Smckusick * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1637488Smckusick * 17*40108Smckusick * @(#)vfs_subr.c 7.27 (Berkeley) 02/16/90 1837488Smckusick */ 1937488Smckusick 2037488Smckusick /* 2137488Smckusick * External virtual filesystem routines 2237488Smckusick */ 2337488Smckusick 2437488Smckusick #include "param.h" 2537488Smckusick #include "mount.h" 2637488Smckusick #include "time.h" 2737488Smckusick #include "vnode.h" 2838265Smckusick #include "namei.h" 2938265Smckusick #include "ucred.h" 3037488Smckusick #include "errno.h" 3139433Smckusick #include "malloc.h" 3237488Smckusick 3337488Smckusick /* 3437488Smckusick * Remove a mount point from the list of mounted filesystems. 3537488Smckusick * Unmount of the root is illegal. 3637488Smckusick */ 3737488Smckusick void 3837488Smckusick vfs_remove(mp) 3937488Smckusick register struct mount *mp; 4037488Smckusick { 4137488Smckusick 4237488Smckusick if (mp == rootfs) 4337488Smckusick panic("vfs_remove: unmounting root"); 4437488Smckusick mp->m_prev->m_next = mp->m_next; 4537488Smckusick mp->m_next->m_prev = mp->m_prev; 4637488Smckusick mp->m_vnodecovered->v_mountedhere = (struct mount *)0; 4737488Smckusick vfs_unlock(mp); 4837488Smckusick } 4937488Smckusick 5037488Smckusick /* 5137488Smckusick * Lock a filesystem. 5237488Smckusick * Used to prevent access to it while mounting and unmounting. 5337488Smckusick */ 5437488Smckusick vfs_lock(mp) 5537488Smckusick register struct mount *mp; 5637488Smckusick { 5737488Smckusick 5839045Smckusick while(mp->m_flag & M_MLOCK) { 5939045Smckusick mp->m_flag |= M_MWAIT; 6039045Smckusick sleep((caddr_t)mp, PVFS); 6139045Smckusick } 6237488Smckusick mp->m_flag |= M_MLOCK; 6337488Smckusick return (0); 6437488Smckusick } 6537488Smckusick 6637488Smckusick /* 6737488Smckusick * Unlock a locked filesystem. 6837488Smckusick * Panic if filesystem is not locked. 6937488Smckusick */ 7037488Smckusick void 7137488Smckusick vfs_unlock(mp) 7237488Smckusick register struct mount *mp; 7337488Smckusick { 7437488Smckusick 7537488Smckusick if ((mp->m_flag & M_MLOCK) == 0) 7637488Smckusick panic("vfs_unlock: locked fs"); 7737488Smckusick mp->m_flag &= ~M_MLOCK; 7837488Smckusick if (mp->m_flag & M_MWAIT) { 7937488Smckusick mp->m_flag &= ~M_MWAIT; 8037488Smckusick wakeup((caddr_t)mp); 8137488Smckusick } 8237488Smckusick } 8337488Smckusick 8437488Smckusick /* 8537488Smckusick * Lookup a mount point by filesystem identifier. 8637488Smckusick */ 8737488Smckusick struct mount * 8837488Smckusick getvfs(fsid) 8937488Smckusick fsid_t *fsid; 9037488Smckusick { 9137488Smckusick register struct mount *mp; 9237488Smckusick 9338288Smckusick mp = rootfs; 9438288Smckusick do { 9537488Smckusick if (mp->m_fsid.val[0] == fsid->val[0] && 9637488Smckusick mp->m_fsid.val[1] == fsid->val[1]) { 9738288Smckusick return (mp); 9837488Smckusick } 9938288Smckusick mp = mp->m_next; 10038288Smckusick } while (mp != rootfs); 10138288Smckusick return ((struct mount *)0); 10237488Smckusick } 10337488Smckusick 10437488Smckusick /* 10537488Smckusick * Set vnode attributes to VNOVAL 10637488Smckusick */ 10737488Smckusick void vattr_null(vap) 10837488Smckusick register struct vattr *vap; 10937488Smckusick { 11037488Smckusick 11137488Smckusick vap->va_type = VNON; 11237488Smckusick vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid = 11337488Smckusick vap->va_fsid = vap->va_fileid = vap->va_size = 11437488Smckusick vap->va_size1 = vap->va_blocksize = vap->va_rdev = 11537488Smckusick vap->va_bytes = vap->va_bytes1 = 11637488Smckusick vap->va_atime.tv_sec = vap->va_atime.tv_usec = 11737488Smckusick vap->va_mtime.tv_sec = vap->va_mtime.tv_usec = 11838258Smckusick vap->va_ctime.tv_sec = vap->va_ctime.tv_usec = 11938258Smckusick vap->va_flags = vap->va_gen = VNOVAL; 12037488Smckusick } 12138265Smckusick 12238265Smckusick /* 12338265Smckusick * Initialize a nameidata structure 12438265Smckusick */ 12538265Smckusick ndinit(ndp) 12638265Smckusick register struct nameidata *ndp; 12738265Smckusick { 12838265Smckusick 12938265Smckusick bzero((caddr_t)ndp, sizeof(struct nameidata)); 13038265Smckusick ndp->ni_iov = &ndp->ni_nd.nd_iovec; 13138265Smckusick ndp->ni_iovcnt = 1; 13238265Smckusick ndp->ni_base = (caddr_t)&ndp->ni_dent; 13338265Smckusick ndp->ni_rw = UIO_WRITE; 13439736Smckusick ndp->ni_uioseg = UIO_SYSSPACE; 13538265Smckusick } 13638265Smckusick 13738265Smckusick /* 13838265Smckusick * Duplicate a nameidata structure 13938265Smckusick */ 14038265Smckusick nddup(ndp, newndp) 14138265Smckusick register struct nameidata *ndp, *newndp; 14238265Smckusick { 14338265Smckusick 14438265Smckusick ndinit(newndp); 14538265Smckusick newndp->ni_cdir = ndp->ni_cdir; 14638348Smckusick VREF(newndp->ni_cdir); 14738265Smckusick newndp->ni_rdir = ndp->ni_rdir; 14838265Smckusick if (newndp->ni_rdir) 14938348Smckusick VREF(newndp->ni_rdir); 15038265Smckusick newndp->ni_cred = ndp->ni_cred; 15138265Smckusick crhold(newndp->ni_cred); 15238265Smckusick } 15338265Smckusick 15438265Smckusick /* 15538265Smckusick * Release a nameidata structure 15638265Smckusick */ 15738265Smckusick ndrele(ndp) 15838265Smckusick register struct nameidata *ndp; 15938265Smckusick { 16038265Smckusick 16138265Smckusick vrele(ndp->ni_cdir); 16238265Smckusick if (ndp->ni_rdir) 16338265Smckusick vrele(ndp->ni_rdir); 16438265Smckusick crfree(ndp->ni_cred); 16538265Smckusick } 16639397Smckusick 16739397Smckusick /* 16839397Smckusick * Routines having to do with the management of the vnode table. 16939397Smckusick */ 17039397Smckusick struct vnode *vfreeh, **vfreet; 17139447Smckusick extern struct vnodeops dead_vnodeops, spec_vnodeops; 17239635Smckusick extern void vclean(); 17339397Smckusick 17439615Smckusick #define SPECHSZ 64 17539615Smckusick #if ((SPECHSZ&(SPECHSZ-1)) == 0) 17639615Smckusick #define SPECHASH(rdev) (((rdev>>5)+(rdev))&(SPECHSZ-1)) 17739615Smckusick #else 17839615Smckusick #define SPECHASH(rdev) (((unsigned)((rdev>>5)+(rdev)))%SPECHSZ) 17939615Smckusick #endif 18039615Smckusick struct vnode *speclisth[SPECHSZ]; 18139615Smckusick 18239397Smckusick /* 18339433Smckusick * Initialize the vnode structures and initialize each file system type. 18439397Smckusick */ 18539433Smckusick vfsinit() 18639397Smckusick { 18739397Smckusick register struct vnode *vp = vnode; 18839433Smckusick struct vfsops **vfsp; 18939397Smckusick 19039433Smckusick /* 19139433Smckusick * Build vnode free list. 19239433Smckusick */ 19339397Smckusick vfreeh = vp; 19439397Smckusick vfreet = &vp->v_freef; 19539397Smckusick vp->v_freeb = &vfreeh; 19639397Smckusick vp->v_op = &dead_vnodeops; 19739945Smckusick vp->v_type = VBAD; 19839397Smckusick for (vp++; vp < vnodeNVNODE; vp++) { 19939397Smckusick *vfreet = vp; 20039397Smckusick vp->v_freeb = vfreet; 20139397Smckusick vfreet = &vp->v_freef; 20239397Smckusick vp->v_op = &dead_vnodeops; 20339945Smckusick vp->v_type = VBAD; 20439397Smckusick } 20539397Smckusick vp--; 20639397Smckusick vp->v_freef = NULL; 20739433Smckusick /* 20839433Smckusick * Initialize the vnode name cache 20939433Smckusick */ 21039433Smckusick nchinit(); 21139433Smckusick /* 21239433Smckusick * Initialize each file system type. 21339433Smckusick */ 21439433Smckusick for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) { 21539433Smckusick if (*vfsp == NULL) 21639433Smckusick continue; 21739433Smckusick (*(*vfsp)->vfs_init)(); 21839433Smckusick } 21939397Smckusick } 22039397Smckusick 22139397Smckusick /* 22239397Smckusick * Return the next vnode from the free list. 22339397Smckusick */ 22439397Smckusick getnewvnode(tag, mp, vops, vpp) 22539397Smckusick enum vtagtype tag; 22639397Smckusick struct mount *mp; 22739397Smckusick struct vnodeops *vops; 22839397Smckusick struct vnode **vpp; 22939397Smckusick { 23039397Smckusick register struct vnode *vp, *vq; 23139397Smckusick 23239397Smckusick if ((vp = vfreeh) == NULL) { 23339397Smckusick tablefull("vnode"); 23439397Smckusick *vpp = 0; 23539397Smckusick return (ENFILE); 23639397Smckusick } 23739809Smckusick if (vp->v_usecount) 23839397Smckusick panic("free vnode isn't"); 23939397Smckusick if (vq = vp->v_freef) 24039397Smckusick vq->v_freeb = &vfreeh; 24139397Smckusick vfreeh = vq; 24239397Smckusick vp->v_freef = NULL; 24339397Smckusick vp->v_freeb = NULL; 24439943Smckusick if (vp->v_type != VBAD) 24539433Smckusick vgone(vp); 24639512Smckusick vp->v_type = VNON; 24739397Smckusick vp->v_flag = 0; 24839397Smckusick vp->v_shlockc = 0; 24939397Smckusick vp->v_exlockc = 0; 25039809Smckusick vp->v_lastr = 0; 25139397Smckusick vp->v_socket = 0; 25239397Smckusick cache_purge(vp); 25339397Smckusick vp->v_tag = tag; 25439433Smckusick vp->v_op = vops; 25539397Smckusick insmntque(vp, mp); 25639397Smckusick VREF(vp); 25739397Smckusick *vpp = vp; 25839397Smckusick return (0); 25939397Smckusick } 26039397Smckusick 26139397Smckusick /* 26239397Smckusick * Move a vnode from one mount queue to another. 26339397Smckusick */ 26439397Smckusick insmntque(vp, mp) 26539397Smckusick register struct vnode *vp; 26639397Smckusick register struct mount *mp; 26739397Smckusick { 26839397Smckusick struct vnode *vq; 26939397Smckusick 27039397Smckusick /* 27139397Smckusick * Delete from old mount point vnode list, if on one. 27239397Smckusick */ 27339397Smckusick if (vp->v_mountb) { 27439397Smckusick if (vq = vp->v_mountf) 27539397Smckusick vq->v_mountb = vp->v_mountb; 27639397Smckusick *vp->v_mountb = vq; 27739397Smckusick } 27839397Smckusick /* 27939397Smckusick * Insert into list of vnodes for the new mount point, if available. 28039397Smckusick */ 28139621Smckusick vp->v_mount = mp; 28239397Smckusick if (mp == NULL) { 28339397Smckusick vp->v_mountf = NULL; 28439397Smckusick vp->v_mountb = NULL; 28539397Smckusick return; 28639397Smckusick } 28739397Smckusick if (mp->m_mounth) { 28839397Smckusick vp->v_mountf = mp->m_mounth; 28939397Smckusick vp->v_mountb = &mp->m_mounth; 29039397Smckusick mp->m_mounth->v_mountb = &vp->v_mountf; 29139397Smckusick mp->m_mounth = vp; 29239397Smckusick } else { 29339397Smckusick mp->m_mounth = vp; 29439397Smckusick vp->v_mountb = &mp->m_mounth; 29539397Smckusick vp->v_mountf = NULL; 29639397Smckusick } 29739397Smckusick } 29839397Smckusick 29939397Smckusick /* 30039433Smckusick * Create a vnode for a block device. 30139433Smckusick * Used for root filesystem, argdev, and swap areas. 30239433Smckusick * Also used for memory file system special devices. 30339397Smckusick */ 30439433Smckusick bdevvp(dev, vpp) 30539433Smckusick dev_t dev; 30639433Smckusick struct vnode **vpp; 30739433Smckusick { 30839433Smckusick register struct vnode *vp; 30939433Smckusick struct vnode *nvp; 31039433Smckusick int error; 31139433Smckusick 31239447Smckusick error = getnewvnode(VT_NON, (struct mount *)0, &spec_vnodeops, &nvp); 31339433Smckusick if (error) { 31439433Smckusick *vpp = 0; 31539433Smckusick return (error); 31639433Smckusick } 31739433Smckusick vp = nvp; 31839433Smckusick vp->v_type = VBLK; 31939615Smckusick if (nvp = checkalias(vp, dev, (struct mount *)0)) { 32039433Smckusick vput(vp); 32139433Smckusick vp = nvp; 32239433Smckusick } 32339433Smckusick *vpp = vp; 32439433Smckusick return (0); 32539433Smckusick } 32639433Smckusick 32739433Smckusick /* 32839433Smckusick * Check to see if the new vnode represents a special device 32939433Smckusick * for which we already have a vnode (either because of 33039433Smckusick * bdevvp() or because of a different vnode representing 33139433Smckusick * the same block device). If such an alias exists, deallocate 33239509Smckusick * the existing contents and return the aliased vnode. The 33339433Smckusick * caller is responsible for filling it with its new contents. 33439433Smckusick */ 33539433Smckusick struct vnode * 33639615Smckusick checkalias(nvp, nvp_rdev, mp) 33739433Smckusick register struct vnode *nvp; 33839615Smckusick dev_t nvp_rdev; 33939433Smckusick struct mount *mp; 34039433Smckusick { 34139433Smckusick register struct vnode *vp; 34239615Smckusick struct vnode **vpp; 34339433Smckusick 34439433Smckusick if (nvp->v_type != VBLK && nvp->v_type != VCHR) 34539433Smckusick return ((struct vnode *)0); 34639615Smckusick 34739615Smckusick vpp = &speclisth[SPECHASH(nvp_rdev)]; 34839433Smckusick loop: 34939615Smckusick for (vp = *vpp; vp; vp = vp->v_specnext) { 35039615Smckusick if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) 35139433Smckusick continue; 35239615Smckusick /* 35339615Smckusick * Alias, but not in use, so flush it out. 35439615Smckusick */ 35539809Smckusick if (vp->v_usecount == 0) { 35639615Smckusick vgone(vp); 35739615Smckusick goto loop; 35839615Smckusick } 35939633Smckusick if (vget(vp)) 36039633Smckusick goto loop; 36139433Smckusick break; 36239433Smckusick } 36339615Smckusick if (vp == NULL || vp->v_tag != VT_NON) { 36439615Smckusick if (vp != NULL) { 36539621Smckusick nvp->v_flag |= VALIASED; 36639615Smckusick vp->v_flag |= VALIASED; 36739621Smckusick vput(vp); 36839615Smckusick } 36939615Smckusick MALLOC(nvp->v_specinfo, struct specinfo *, 37039615Smckusick sizeof(struct specinfo), M_VNODE, M_WAITOK); 37139615Smckusick nvp->v_rdev = nvp_rdev; 37239809Smckusick nvp->v_hashchain = vpp; 37339615Smckusick nvp->v_specnext = *vpp; 37439615Smckusick *vpp = nvp; 37539433Smckusick return ((struct vnode *)0); 37639433Smckusick } 37739484Smckusick VOP_UNLOCK(vp); 37839484Smckusick vclean(vp, 0); 37939433Smckusick vp->v_op = nvp->v_op; 38039433Smckusick vp->v_tag = nvp->v_tag; 38139433Smckusick nvp->v_type = VNON; 38239433Smckusick insmntque(vp, mp); 38339433Smckusick return (vp); 38439433Smckusick } 38539433Smckusick 38639433Smckusick /* 38739433Smckusick * Grab a particular vnode from the free list, increment its 38839433Smckusick * reference count and lock it. The vnode lock bit is set the 38939433Smckusick * vnode is being eliminated in vgone. The process is awakened 39039433Smckusick * when the transition is completed, and an error returned to 39139433Smckusick * indicate that the vnode is no longer usable (possibly having 39239433Smckusick * been changed to a new file system type). 39339433Smckusick */ 39439397Smckusick vget(vp) 39539397Smckusick register struct vnode *vp; 39639397Smckusick { 39739397Smckusick register struct vnode *vq; 39839397Smckusick 39939433Smckusick if (vp->v_flag & VXLOCK) { 40039433Smckusick vp->v_flag |= VXWANT; 40139433Smckusick sleep((caddr_t)vp, PINOD); 40239433Smckusick return (1); 40339433Smckusick } 40439809Smckusick if (vp->v_usecount == 0) { 40539433Smckusick if (vq = vp->v_freef) 40639433Smckusick vq->v_freeb = vp->v_freeb; 40739433Smckusick else 40839433Smckusick vfreet = vp->v_freeb; 40939433Smckusick *vp->v_freeb = vq; 41039433Smckusick vp->v_freef = NULL; 41139433Smckusick vp->v_freeb = NULL; 41239433Smckusick } 41339397Smckusick VREF(vp); 41439433Smckusick VOP_LOCK(vp); 41539433Smckusick return (0); 41639397Smckusick } 41739397Smckusick 41839397Smckusick /* 41939397Smckusick * Vnode reference, just increment the count 42039397Smckusick */ 42139397Smckusick void vref(vp) 42239397Smckusick struct vnode *vp; 42339397Smckusick { 42439397Smckusick 42539809Smckusick vp->v_usecount++; 42639397Smckusick } 42739397Smckusick 42839397Smckusick /* 42939397Smckusick * vput(), just unlock and vrele() 43039397Smckusick */ 43139397Smckusick void vput(vp) 43239397Smckusick register struct vnode *vp; 43339397Smckusick { 43439397Smckusick VOP_UNLOCK(vp); 43539397Smckusick vrele(vp); 43639397Smckusick } 43739397Smckusick 43839397Smckusick /* 43939397Smckusick * Vnode release. 44039397Smckusick * If count drops to zero, call inactive routine and return to freelist. 44139397Smckusick */ 44239397Smckusick void vrele(vp) 44339397Smckusick register struct vnode *vp; 44439397Smckusick { 44539397Smckusick 44639397Smckusick if (vp == NULL) 44739433Smckusick panic("vrele: null vp"); 44839809Smckusick vp->v_usecount--; 44939809Smckusick if (vp->v_usecount < 0) 45039667Smckusick vprint("vrele: bad ref count", vp); 45139809Smckusick if (vp->v_usecount > 0) 45239397Smckusick return; 45339397Smckusick if (vfreeh == (struct vnode *)0) { 45439397Smckusick /* 45539397Smckusick * insert into empty list 45639397Smckusick */ 45739397Smckusick vfreeh = vp; 45839397Smckusick vp->v_freeb = &vfreeh; 45939397Smckusick } else { 46039397Smckusick /* 46139397Smckusick * insert at tail of list 46239397Smckusick */ 46339397Smckusick *vfreet = vp; 46439397Smckusick vp->v_freeb = vfreet; 46539397Smckusick } 46639433Smckusick vp->v_freef = NULL; 46739433Smckusick vfreet = &vp->v_freef; 46839433Smckusick VOP_INACTIVE(vp); 46939397Smckusick } 47039433Smckusick 47139433Smckusick /* 47239809Smckusick * Page or buffer structure gets a reference. 47339809Smckusick */ 47439809Smckusick vhold(vp) 47539809Smckusick register struct vnode *vp; 47639809Smckusick { 47739809Smckusick 47839809Smckusick vp->v_holdcnt++; 47939809Smckusick } 48039809Smckusick 48139809Smckusick /* 48239809Smckusick * Page or buffer structure frees a reference. 48339809Smckusick */ 48439809Smckusick holdrele(vp) 48539809Smckusick register struct vnode *vp; 48639809Smckusick { 48739809Smckusick 48839809Smckusick if (vp->v_holdcnt <= 0) 48939809Smckusick panic("holdrele: holdcnt"); 49039809Smckusick vp->v_holdcnt--; 49139809Smckusick } 49239809Smckusick 49339809Smckusick /* 49439509Smckusick * Remove any vnodes in the vnode table belonging to mount point mp. 49539509Smckusick * 49639509Smckusick * If MNT_NOFORCE is specified, there should not be any active ones, 49739509Smckusick * return error if any are found (nb: this is a user error, not a 49839509Smckusick * system error). If MNT_FORCE is specified, detach any active vnodes 49939509Smckusick * that are found. 50039509Smckusick */ 50139509Smckusick int busyprt = 0; /* patch to print out busy vnodes */ 50239509Smckusick 50339509Smckusick vflush(mp, skipvp, flags) 50439509Smckusick struct mount *mp; 50539509Smckusick struct vnode *skipvp; 50639509Smckusick int flags; 50739509Smckusick { 50839509Smckusick register struct vnode *vp, *nvp; 50939509Smckusick int busy = 0; 51039509Smckusick 51139509Smckusick for (vp = mp->m_mounth; vp; vp = nvp) { 51239509Smckusick nvp = vp->v_mountf; 51339509Smckusick /* 51439509Smckusick * Skip over a selected vnode. 51539509Smckusick * Used by ufs to skip over the quota structure inode. 51639509Smckusick */ 51739509Smckusick if (vp == skipvp) 51839509Smckusick continue; 51939509Smckusick /* 52039809Smckusick * With v_usecount == 0, all we need to do is clear 52139509Smckusick * out the vnode data structures and we are done. 52239509Smckusick */ 52339809Smckusick if (vp->v_usecount == 0) { 52439509Smckusick vgone(vp); 52539509Smckusick continue; 52639509Smckusick } 52739509Smckusick /* 52839509Smckusick * For block or character devices, revert to an 52939509Smckusick * anonymous device. For all other files, just kill them. 53039509Smckusick */ 53139509Smckusick if (flags & MNT_FORCE) { 53239509Smckusick if (vp->v_type != VBLK && vp->v_type != VCHR) { 53339509Smckusick vgone(vp); 53439509Smckusick } else { 53539509Smckusick vclean(vp, 0); 53639509Smckusick vp->v_op = &spec_vnodeops; 53739509Smckusick insmntque(vp, (struct mount *)0); 53839509Smckusick } 53939509Smckusick continue; 54039509Smckusick } 54139509Smckusick if (busyprt) 54239667Smckusick vprint("vflush: busy vnode", vp); 54339509Smckusick busy++; 54439509Smckusick } 54539509Smckusick if (busy) 54639509Smckusick return (EBUSY); 54739509Smckusick return (0); 54839509Smckusick } 54939509Smckusick 55039509Smckusick /* 55139433Smckusick * Disassociate the underlying file system from a vnode. 55239433Smckusick */ 55339484Smckusick void vclean(vp, doclose) 55439433Smckusick register struct vnode *vp; 55539484Smckusick long doclose; 55639433Smckusick { 55739433Smckusick struct vnodeops *origops; 55839484Smckusick int active; 55939433Smckusick 56039484Smckusick /* 56139484Smckusick * Check to see if the vnode is in use. 56239667Smckusick * If so we have to reference it before we clean it out 56339667Smckusick * so that its count cannot fall to zero and generate a 56439667Smckusick * race against ourselves to recycle it. 56539484Smckusick */ 56639809Smckusick if (active = vp->v_usecount) 56739484Smckusick VREF(vp); 56839484Smckusick /* 56939484Smckusick * Prevent the vnode from being recycled or 57039484Smckusick * brought into use while we clean it out. 57139484Smckusick */ 57239667Smckusick if (vp->v_flag & VXLOCK) 57339667Smckusick panic("vclean: deadlock"); 57439433Smckusick vp->v_flag |= VXLOCK; 57539433Smckusick /* 57639667Smckusick * Even if the count is zero, the VOP_INACTIVE routine may still 57739667Smckusick * have the object locked while it cleans it out. The VOP_LOCK 57839667Smckusick * ensures that the VOP_INACTIVE routine is done with its work. 57939667Smckusick * For active vnodes, it ensures that no other activity can 58039667Smckusick * occur while the buffer list is being cleaned out. 58139667Smckusick */ 58239667Smckusick VOP_LOCK(vp); 58339667Smckusick if (doclose) 58439667Smckusick vinvalbuf(vp, 1); 58539667Smckusick /* 58639433Smckusick * Prevent any further operations on the vnode from 58739433Smckusick * being passed through to the old file system. 58839433Smckusick */ 58939433Smckusick origops = vp->v_op; 59039433Smckusick vp->v_op = &dead_vnodeops; 59139433Smckusick vp->v_tag = VT_NON; 59239433Smckusick /* 59339484Smckusick * If purging an active vnode, it must be unlocked, closed, 59439484Smckusick * and deactivated before being reclaimed. 59539433Smckusick */ 59639667Smckusick (*(origops->vn_unlock))(vp); 59739484Smckusick if (active) { 59839484Smckusick if (doclose) 59939484Smckusick (*(origops->vn_close))(vp, 0, NOCRED); 60039433Smckusick (*(origops->vn_inactive))(vp); 60139433Smckusick } 60239433Smckusick /* 60339433Smckusick * Reclaim the vnode. 60439433Smckusick */ 60539433Smckusick if ((*(origops->vn_reclaim))(vp)) 60639433Smckusick panic("vclean: cannot reclaim"); 60739484Smckusick if (active) 60839484Smckusick vrele(vp); 60939433Smckusick /* 61039433Smckusick * Done with purge, notify sleepers in vget of the grim news. 61139433Smckusick */ 61239433Smckusick vp->v_flag &= ~VXLOCK; 61339433Smckusick if (vp->v_flag & VXWANT) { 61439433Smckusick vp->v_flag &= ~VXWANT; 61539433Smckusick wakeup((caddr_t)vp); 61639433Smckusick } 61739433Smckusick } 61839433Smckusick 61939433Smckusick /* 62039633Smckusick * Eliminate all activity associated with the requested vnode 62139633Smckusick * and with all vnodes aliased to the requested vnode. 62239633Smckusick */ 62339633Smckusick void vgoneall(vp) 62439633Smckusick register struct vnode *vp; 62539633Smckusick { 62639809Smckusick register struct vnode *vq; 62739633Smckusick 62839809Smckusick while (vp->v_flag & VALIASED) { 62939809Smckusick for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 63040050Smckusick if (vq->v_rdev != vp->v_rdev || 63140050Smckusick vq->v_type != vp->v_type || vp == vq) 63239633Smckusick continue; 63339633Smckusick vgone(vq); 63439809Smckusick break; 63539633Smckusick } 63639633Smckusick } 63739633Smckusick vgone(vp); 63839633Smckusick } 63939633Smckusick 64039633Smckusick /* 64139433Smckusick * Eliminate all activity associated with a vnode 64239433Smckusick * in preparation for reuse. 64339433Smckusick */ 64439433Smckusick void vgone(vp) 64539433Smckusick register struct vnode *vp; 64639433Smckusick { 64739809Smckusick register struct vnode *vq; 64839615Smckusick struct vnode *vx; 64939615Smckusick long count; 65039433Smckusick 65139433Smckusick /* 65239433Smckusick * Clean out the filesystem specific data. 65339433Smckusick */ 65439484Smckusick vclean(vp, 1); 65539433Smckusick /* 65639433Smckusick * Delete from old mount point vnode list, if on one. 65739433Smckusick */ 65839433Smckusick if (vp->v_mountb) { 65939433Smckusick if (vq = vp->v_mountf) 66039433Smckusick vq->v_mountb = vp->v_mountb; 66139433Smckusick *vp->v_mountb = vq; 66239433Smckusick vp->v_mountf = NULL; 66339433Smckusick vp->v_mountb = NULL; 66439433Smckusick } 66539433Smckusick /* 66639433Smckusick * If special device, remove it from special device alias list. 66739433Smckusick */ 66839433Smckusick if (vp->v_type == VBLK || vp->v_type == VCHR) { 66939809Smckusick if (*vp->v_hashchain == vp) { 67039809Smckusick *vp->v_hashchain = vp->v_specnext; 67139433Smckusick } else { 67239809Smckusick for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 67339615Smckusick if (vq->v_specnext != vp) 67439433Smckusick continue; 67539615Smckusick vq->v_specnext = vp->v_specnext; 67639433Smckusick break; 67739433Smckusick } 67839615Smckusick if (vq == NULL) 67939433Smckusick panic("missing bdev"); 68039433Smckusick } 68139615Smckusick if (vp->v_flag & VALIASED) { 68239809Smckusick count = 0; 68339809Smckusick for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 684*40108Smckusick if (vq->v_rdev != vp->v_rdev || 685*40108Smckusick vq->v_type != vp->v_type) 68639615Smckusick continue; 68739615Smckusick count++; 68839615Smckusick vx = vq; 68939615Smckusick } 69039615Smckusick if (count == 0) 69139615Smckusick panic("missing alias"); 69239615Smckusick if (count == 1) 69339615Smckusick vx->v_flag &= ~VALIASED; 69439615Smckusick vp->v_flag &= ~VALIASED; 69539615Smckusick } 69639615Smckusick FREE(vp->v_specinfo, M_VNODE); 69739615Smckusick vp->v_specinfo = NULL; 69839433Smckusick } 69939433Smckusick /* 70039433Smckusick * If it is on the freelist, move it to the head of the list. 70139433Smckusick */ 70239433Smckusick if (vp->v_freeb) { 70339433Smckusick if (vq = vp->v_freef) 70439433Smckusick vq->v_freeb = vp->v_freeb; 70539433Smckusick else 70639433Smckusick vfreet = vp->v_freeb; 70739433Smckusick *vp->v_freeb = vq; 70839433Smckusick vp->v_freef = vfreeh; 70939433Smckusick vp->v_freeb = &vfreeh; 71039433Smckusick vfreeh->v_freeb = &vp->v_freef; 71139433Smckusick vfreeh = vp; 71239433Smckusick } 71339484Smckusick vp->v_type = VBAD; 71439433Smckusick } 71539633Smckusick 71639633Smckusick /* 71739821Smckusick * Lookup a vnode by device number. 71839821Smckusick */ 71939821Smckusick vfinddev(dev, type, vpp) 72039821Smckusick dev_t dev; 72139821Smckusick enum vtype type; 72239821Smckusick struct vnode **vpp; 72339821Smckusick { 72439821Smckusick register struct vnode *vp; 72539821Smckusick 72639821Smckusick for (vp = speclisth[SPECHASH(dev)]; vp; vp = vp->v_specnext) { 72739821Smckusick if (dev != vp->v_rdev || type != vp->v_type) 72839821Smckusick continue; 72939821Smckusick *vpp = vp; 73039821Smckusick return (0); 73139821Smckusick } 73239821Smckusick return (1); 73339821Smckusick } 73439821Smckusick 73539821Smckusick /* 73639633Smckusick * Calculate the total number of references to a special device. 73739633Smckusick */ 73839633Smckusick vcount(vp) 73939633Smckusick register struct vnode *vp; 74039633Smckusick { 74139809Smckusick register struct vnode *vq; 74239633Smckusick int count; 74339633Smckusick 74439633Smckusick if ((vp->v_flag & VALIASED) == 0) 74539809Smckusick return (vp->v_usecount); 74639633Smckusick loop: 74739809Smckusick for (count = 0, vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 748*40108Smckusick if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) 74939633Smckusick continue; 75039633Smckusick /* 75139633Smckusick * Alias, but not in use, so flush it out. 75239633Smckusick */ 75339809Smckusick if (vq->v_usecount == 0) { 75439633Smckusick vgone(vq); 75539633Smckusick goto loop; 75639633Smckusick } 75739809Smckusick count += vq->v_usecount; 75839633Smckusick } 75939633Smckusick return (count); 76039633Smckusick } 76139667Smckusick 76239667Smckusick /* 76339667Smckusick * Print out a description of a vnode. 76439667Smckusick */ 76539667Smckusick static char *typename[] = 76639667Smckusick { "VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VBAD" }; 76739667Smckusick 76839667Smckusick vprint(label, vp) 76939667Smckusick char *label; 77039667Smckusick register struct vnode *vp; 77139667Smckusick { 77239913Smckusick char buf[64]; 77339667Smckusick 77439667Smckusick if (label != NULL) 77539667Smckusick printf("%s: ", label); 77639913Smckusick printf("type %s, usecount %d, refcount %d,", typename[vp->v_type], 77739809Smckusick vp->v_usecount, vp->v_holdcnt); 77839913Smckusick buf[0] = '\0'; 77939913Smckusick if (vp->v_flag & VROOT) 78039913Smckusick strcat(buf, "|VROOT"); 78139913Smckusick if (vp->v_flag & VTEXT) 78239913Smckusick strcat(buf, "|VTEXT"); 78339913Smckusick if (vp->v_flag & VXLOCK) 78439913Smckusick strcat(buf, "|VXLOCK"); 78539913Smckusick if (vp->v_flag & VXWANT) 78639913Smckusick strcat(buf, "|VXWANT"); 78739913Smckusick if (vp->v_flag & VEXLOCK) 78839913Smckusick strcat(buf, "|VEXLOCK"); 78939913Smckusick if (vp->v_flag & VSHLOCK) 79039913Smckusick strcat(buf, "|VSHLOCK"); 79139913Smckusick if (vp->v_flag & VLWAIT) 79239913Smckusick strcat(buf, "|VLWAIT"); 79339913Smckusick if (vp->v_flag & VALIASED) 79439913Smckusick strcat(buf, "|VALIASED"); 79539913Smckusick if (vp->v_flag & VBWAIT) 79639913Smckusick strcat(buf, "|VBWAIT"); 79739913Smckusick if (buf[0] != '\0') 79839913Smckusick printf(" flags (%s)", &buf[1]); 79939913Smckusick printf("\n\t"); 80039667Smckusick VOP_PRINT(vp); 80139667Smckusick } 80239913Smckusick 80339913Smckusick strcat(src, append) 80439913Smckusick register char *src, *append; 80539913Smckusick { 80639913Smckusick 80739913Smckusick for (; *src; ++src) 80839913Smckusick /* void */; 80939913Smckusick while (*src++ = *append++) 81039913Smckusick /* void */; 81139913Smckusick } 812