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*40652Smckusick * @(#)vfs_subr.c 7.35 (Berkeley) 03/27/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" 28*40652Smckusick #include "specdev.h" 2938265Smckusick #include "namei.h" 3038265Smckusick #include "ucred.h" 3137488Smckusick #include "errno.h" 3239433Smckusick #include "malloc.h" 3337488Smckusick 3437488Smckusick /* 3537488Smckusick * Remove a mount point from the list of mounted filesystems. 3637488Smckusick * Unmount of the root is illegal. 3737488Smckusick */ 3837488Smckusick void 3937488Smckusick vfs_remove(mp) 4037488Smckusick register struct mount *mp; 4137488Smckusick { 4237488Smckusick 4337488Smckusick if (mp == rootfs) 4437488Smckusick panic("vfs_remove: unmounting root"); 4537488Smckusick mp->m_prev->m_next = mp->m_next; 4637488Smckusick mp->m_next->m_prev = mp->m_prev; 4737488Smckusick mp->m_vnodecovered->v_mountedhere = (struct mount *)0; 4837488Smckusick vfs_unlock(mp); 4937488Smckusick } 5037488Smckusick 5137488Smckusick /* 5237488Smckusick * Lock a filesystem. 5337488Smckusick * Used to prevent access to it while mounting and unmounting. 5437488Smckusick */ 5537488Smckusick vfs_lock(mp) 5637488Smckusick register struct mount *mp; 5737488Smckusick { 5837488Smckusick 5939045Smckusick while(mp->m_flag & M_MLOCK) { 6039045Smckusick mp->m_flag |= M_MWAIT; 6139045Smckusick sleep((caddr_t)mp, PVFS); 6239045Smckusick } 6337488Smckusick mp->m_flag |= M_MLOCK; 6437488Smckusick return (0); 6537488Smckusick } 6637488Smckusick 6737488Smckusick /* 6837488Smckusick * Unlock a locked filesystem. 6937488Smckusick * Panic if filesystem is not locked. 7037488Smckusick */ 7137488Smckusick void 7237488Smckusick vfs_unlock(mp) 7337488Smckusick register struct mount *mp; 7437488Smckusick { 7537488Smckusick 7637488Smckusick if ((mp->m_flag & M_MLOCK) == 0) 7737488Smckusick panic("vfs_unlock: locked fs"); 7837488Smckusick mp->m_flag &= ~M_MLOCK; 7937488Smckusick if (mp->m_flag & M_MWAIT) { 8037488Smckusick mp->m_flag &= ~M_MWAIT; 8137488Smckusick wakeup((caddr_t)mp); 8237488Smckusick } 8337488Smckusick } 8437488Smckusick 8537488Smckusick /* 8637488Smckusick * Lookup a mount point by filesystem identifier. 8737488Smckusick */ 8837488Smckusick struct mount * 8937488Smckusick getvfs(fsid) 9037488Smckusick fsid_t *fsid; 9137488Smckusick { 9237488Smckusick register struct mount *mp; 9337488Smckusick 9438288Smckusick mp = rootfs; 9538288Smckusick do { 9640342Smckusick if (mp->m_stat.f_fsid.val[0] == fsid->val[0] && 9740342Smckusick mp->m_stat.f_fsid.val[1] == fsid->val[1]) { 9838288Smckusick return (mp); 9937488Smckusick } 10038288Smckusick mp = mp->m_next; 10138288Smckusick } while (mp != rootfs); 10238288Smckusick return ((struct mount *)0); 10337488Smckusick } 10437488Smckusick 10537488Smckusick /* 10637488Smckusick * Set vnode attributes to VNOVAL 10737488Smckusick */ 10837488Smckusick void vattr_null(vap) 10937488Smckusick register struct vattr *vap; 11037488Smckusick { 11137488Smckusick 11237488Smckusick vap->va_type = VNON; 11337488Smckusick vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid = 11437488Smckusick vap->va_fsid = vap->va_fileid = vap->va_size = 11540643Smckusick vap->va_size_rsv = vap->va_blocksize = vap->va_rdev = 11640643Smckusick vap->va_bytes = vap->va_bytes_rsv = 11737488Smckusick vap->va_atime.tv_sec = vap->va_atime.tv_usec = 11837488Smckusick vap->va_mtime.tv_sec = vap->va_mtime.tv_usec = 11938258Smckusick vap->va_ctime.tv_sec = vap->va_ctime.tv_usec = 12038258Smckusick vap->va_flags = vap->va_gen = VNOVAL; 12137488Smckusick } 12238265Smckusick 12338265Smckusick /* 12438265Smckusick * Initialize a nameidata structure 12538265Smckusick */ 12638265Smckusick ndinit(ndp) 12738265Smckusick register struct nameidata *ndp; 12838265Smckusick { 12938265Smckusick 13038265Smckusick bzero((caddr_t)ndp, sizeof(struct nameidata)); 13138265Smckusick ndp->ni_iov = &ndp->ni_nd.nd_iovec; 13238265Smckusick ndp->ni_iovcnt = 1; 13338265Smckusick ndp->ni_base = (caddr_t)&ndp->ni_dent; 13438265Smckusick ndp->ni_rw = UIO_WRITE; 13539736Smckusick ndp->ni_uioseg = UIO_SYSSPACE; 13638265Smckusick } 13738265Smckusick 13838265Smckusick /* 13938265Smckusick * Duplicate a nameidata structure 14038265Smckusick */ 14138265Smckusick nddup(ndp, newndp) 14238265Smckusick register struct nameidata *ndp, *newndp; 14338265Smckusick { 14438265Smckusick 14538265Smckusick ndinit(newndp); 14638265Smckusick newndp->ni_cdir = ndp->ni_cdir; 14738348Smckusick VREF(newndp->ni_cdir); 14838265Smckusick newndp->ni_rdir = ndp->ni_rdir; 14938265Smckusick if (newndp->ni_rdir) 15038348Smckusick VREF(newndp->ni_rdir); 15138265Smckusick newndp->ni_cred = ndp->ni_cred; 15238265Smckusick crhold(newndp->ni_cred); 15338265Smckusick } 15438265Smckusick 15538265Smckusick /* 15638265Smckusick * Release a nameidata structure 15738265Smckusick */ 15838265Smckusick ndrele(ndp) 15938265Smckusick register struct nameidata *ndp; 16038265Smckusick { 16138265Smckusick 16238265Smckusick vrele(ndp->ni_cdir); 16338265Smckusick if (ndp->ni_rdir) 16438265Smckusick vrele(ndp->ni_rdir); 16538265Smckusick crfree(ndp->ni_cred); 16638265Smckusick } 16739397Smckusick 16839397Smckusick /* 16939397Smckusick * Routines having to do with the management of the vnode table. 17039397Smckusick */ 17139397Smckusick struct vnode *vfreeh, **vfreet; 17239447Smckusick extern struct vnodeops dead_vnodeops, spec_vnodeops; 17339635Smckusick extern void vclean(); 17439397Smckusick 17539397Smckusick /* 17639433Smckusick * Initialize the vnode structures and initialize each file system type. 17739397Smckusick */ 17839433Smckusick vfsinit() 17939397Smckusick { 18039397Smckusick register struct vnode *vp = vnode; 18139433Smckusick struct vfsops **vfsp; 18239397Smckusick 18339433Smckusick /* 18439433Smckusick * Build vnode free list. 18539433Smckusick */ 18639397Smckusick vfreeh = vp; 18739397Smckusick vfreet = &vp->v_freef; 18839397Smckusick vp->v_freeb = &vfreeh; 18939397Smckusick vp->v_op = &dead_vnodeops; 19039945Smckusick vp->v_type = VBAD; 19139397Smckusick for (vp++; vp < vnodeNVNODE; vp++) { 19239397Smckusick *vfreet = vp; 19339397Smckusick vp->v_freeb = vfreet; 19439397Smckusick vfreet = &vp->v_freef; 19539397Smckusick vp->v_op = &dead_vnodeops; 19639945Smckusick vp->v_type = VBAD; 19739397Smckusick } 19839397Smckusick vp--; 19939397Smckusick vp->v_freef = NULL; 20039433Smckusick /* 20139433Smckusick * Initialize the vnode name cache 20239433Smckusick */ 20339433Smckusick nchinit(); 20439433Smckusick /* 20539433Smckusick * Initialize each file system type. 20639433Smckusick */ 20739433Smckusick for (vfsp = &vfssw[0]; vfsp <= &vfssw[MOUNT_MAXTYPE]; vfsp++) { 20839433Smckusick if (*vfsp == NULL) 20939433Smckusick continue; 21039433Smckusick (*(*vfsp)->vfs_init)(); 21139433Smckusick } 21239397Smckusick } 21339397Smckusick 21439397Smckusick /* 21539397Smckusick * Return the next vnode from the free list. 21639397Smckusick */ 21739397Smckusick getnewvnode(tag, mp, vops, vpp) 21839397Smckusick enum vtagtype tag; 21939397Smckusick struct mount *mp; 22039397Smckusick struct vnodeops *vops; 22139397Smckusick struct vnode **vpp; 22239397Smckusick { 22339397Smckusick register struct vnode *vp, *vq; 22439397Smckusick 22539397Smckusick if ((vp = vfreeh) == NULL) { 22639397Smckusick tablefull("vnode"); 22739397Smckusick *vpp = 0; 22839397Smckusick return (ENFILE); 22939397Smckusick } 23039809Smckusick if (vp->v_usecount) 23139397Smckusick panic("free vnode isn't"); 23239397Smckusick if (vq = vp->v_freef) 23339397Smckusick vq->v_freeb = &vfreeh; 23439397Smckusick vfreeh = vq; 23539397Smckusick vp->v_freef = NULL; 23639397Smckusick vp->v_freeb = NULL; 23739943Smckusick if (vp->v_type != VBAD) 23839433Smckusick vgone(vp); 23939512Smckusick vp->v_type = VNON; 24039397Smckusick vp->v_flag = 0; 24139397Smckusick vp->v_shlockc = 0; 24239397Smckusick vp->v_exlockc = 0; 24339809Smckusick vp->v_lastr = 0; 24439397Smckusick vp->v_socket = 0; 24539397Smckusick cache_purge(vp); 24639397Smckusick vp->v_tag = tag; 24739433Smckusick vp->v_op = vops; 24839397Smckusick insmntque(vp, mp); 24939397Smckusick VREF(vp); 25039397Smckusick *vpp = vp; 25139397Smckusick return (0); 25239397Smckusick } 25339397Smckusick 25439397Smckusick /* 25539397Smckusick * Move a vnode from one mount queue to another. 25639397Smckusick */ 25739397Smckusick insmntque(vp, mp) 25839397Smckusick register struct vnode *vp; 25939397Smckusick register struct mount *mp; 26039397Smckusick { 26139397Smckusick struct vnode *vq; 26239397Smckusick 26339397Smckusick /* 26439397Smckusick * Delete from old mount point vnode list, if on one. 26539397Smckusick */ 26639397Smckusick if (vp->v_mountb) { 26739397Smckusick if (vq = vp->v_mountf) 26839397Smckusick vq->v_mountb = vp->v_mountb; 26939397Smckusick *vp->v_mountb = vq; 27039397Smckusick } 27139397Smckusick /* 27239397Smckusick * Insert into list of vnodes for the new mount point, if available. 27339397Smckusick */ 27439621Smckusick vp->v_mount = mp; 27539397Smckusick if (mp == NULL) { 27639397Smckusick vp->v_mountf = NULL; 27739397Smckusick vp->v_mountb = NULL; 27839397Smckusick return; 27939397Smckusick } 28039397Smckusick if (mp->m_mounth) { 28139397Smckusick vp->v_mountf = mp->m_mounth; 28239397Smckusick vp->v_mountb = &mp->m_mounth; 28339397Smckusick mp->m_mounth->v_mountb = &vp->v_mountf; 28439397Smckusick mp->m_mounth = vp; 28539397Smckusick } else { 28639397Smckusick mp->m_mounth = vp; 28739397Smckusick vp->v_mountb = &mp->m_mounth; 28839397Smckusick vp->v_mountf = NULL; 28939397Smckusick } 29039397Smckusick } 29139397Smckusick 29239397Smckusick /* 29339433Smckusick * Create a vnode for a block device. 29439433Smckusick * Used for root filesystem, argdev, and swap areas. 29539433Smckusick * Also used for memory file system special devices. 29639397Smckusick */ 29739433Smckusick bdevvp(dev, vpp) 29839433Smckusick dev_t dev; 29939433Smckusick struct vnode **vpp; 30039433Smckusick { 30139433Smckusick register struct vnode *vp; 30239433Smckusick struct vnode *nvp; 30339433Smckusick int error; 30439433Smckusick 30539447Smckusick error = getnewvnode(VT_NON, (struct mount *)0, &spec_vnodeops, &nvp); 30639433Smckusick if (error) { 30739433Smckusick *vpp = 0; 30839433Smckusick return (error); 30939433Smckusick } 31039433Smckusick vp = nvp; 31139433Smckusick vp->v_type = VBLK; 31239615Smckusick if (nvp = checkalias(vp, dev, (struct mount *)0)) { 31339433Smckusick vput(vp); 31439433Smckusick vp = nvp; 31539433Smckusick } 31639433Smckusick *vpp = vp; 31739433Smckusick return (0); 31839433Smckusick } 31939433Smckusick 32039433Smckusick /* 32139433Smckusick * Check to see if the new vnode represents a special device 32239433Smckusick * for which we already have a vnode (either because of 32339433Smckusick * bdevvp() or because of a different vnode representing 32439433Smckusick * the same block device). If such an alias exists, deallocate 32539509Smckusick * the existing contents and return the aliased vnode. The 32639433Smckusick * caller is responsible for filling it with its new contents. 32739433Smckusick */ 32839433Smckusick struct vnode * 32939615Smckusick checkalias(nvp, nvp_rdev, mp) 33039433Smckusick register struct vnode *nvp; 33139615Smckusick dev_t nvp_rdev; 33239433Smckusick struct mount *mp; 33339433Smckusick { 33439433Smckusick register struct vnode *vp; 33539615Smckusick struct vnode **vpp; 33639433Smckusick 33739433Smckusick if (nvp->v_type != VBLK && nvp->v_type != VCHR) 33839433Smckusick return ((struct vnode *)0); 33939615Smckusick 34039615Smckusick vpp = &speclisth[SPECHASH(nvp_rdev)]; 34139433Smckusick loop: 34239615Smckusick for (vp = *vpp; vp; vp = vp->v_specnext) { 34339615Smckusick if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) 34439433Smckusick continue; 34539615Smckusick /* 34639615Smckusick * Alias, but not in use, so flush it out. 34739615Smckusick */ 34839809Smckusick if (vp->v_usecount == 0) { 34939615Smckusick vgone(vp); 35039615Smckusick goto loop; 35139615Smckusick } 35239633Smckusick if (vget(vp)) 35339633Smckusick goto loop; 35439433Smckusick break; 35539433Smckusick } 35639615Smckusick if (vp == NULL || vp->v_tag != VT_NON) { 35739615Smckusick MALLOC(nvp->v_specinfo, struct specinfo *, 35839615Smckusick sizeof(struct specinfo), M_VNODE, M_WAITOK); 35939615Smckusick nvp->v_rdev = nvp_rdev; 36039809Smckusick nvp->v_hashchain = vpp; 36139615Smckusick nvp->v_specnext = *vpp; 36239615Smckusick *vpp = nvp; 36340640Smckusick if (vp != NULL) { 36440640Smckusick nvp->v_flag |= VALIASED; 36540640Smckusick vp->v_flag |= VALIASED; 36640640Smckusick vput(vp); 36740640Smckusick } 36839433Smckusick return ((struct vnode *)0); 36939433Smckusick } 37039484Smckusick VOP_UNLOCK(vp); 37139484Smckusick vclean(vp, 0); 37239433Smckusick vp->v_op = nvp->v_op; 37339433Smckusick vp->v_tag = nvp->v_tag; 37439433Smckusick nvp->v_type = VNON; 37539433Smckusick insmntque(vp, mp); 37639433Smckusick return (vp); 37739433Smckusick } 37839433Smckusick 37939433Smckusick /* 38039433Smckusick * Grab a particular vnode from the free list, increment its 38139433Smckusick * reference count and lock it. The vnode lock bit is set the 38239433Smckusick * vnode is being eliminated in vgone. The process is awakened 38339433Smckusick * when the transition is completed, and an error returned to 38439433Smckusick * indicate that the vnode is no longer usable (possibly having 38539433Smckusick * been changed to a new file system type). 38639433Smckusick */ 38739397Smckusick vget(vp) 38839397Smckusick register struct vnode *vp; 38939397Smckusick { 39039397Smckusick register struct vnode *vq; 39139397Smckusick 39239433Smckusick if (vp->v_flag & VXLOCK) { 39339433Smckusick vp->v_flag |= VXWANT; 39439433Smckusick sleep((caddr_t)vp, PINOD); 39539433Smckusick return (1); 39639433Smckusick } 39739809Smckusick if (vp->v_usecount == 0) { 39839433Smckusick if (vq = vp->v_freef) 39939433Smckusick vq->v_freeb = vp->v_freeb; 40039433Smckusick else 40139433Smckusick vfreet = vp->v_freeb; 40239433Smckusick *vp->v_freeb = vq; 40339433Smckusick vp->v_freef = NULL; 40439433Smckusick vp->v_freeb = NULL; 40539433Smckusick } 40639397Smckusick VREF(vp); 40739433Smckusick VOP_LOCK(vp); 40839433Smckusick return (0); 40939397Smckusick } 41039397Smckusick 41139397Smckusick /* 41239397Smckusick * Vnode reference, just increment the count 41339397Smckusick */ 41439397Smckusick void vref(vp) 41539397Smckusick struct vnode *vp; 41639397Smckusick { 41739397Smckusick 41839809Smckusick vp->v_usecount++; 41939397Smckusick } 42039397Smckusick 42139397Smckusick /* 42239397Smckusick * vput(), just unlock and vrele() 42339397Smckusick */ 42439397Smckusick void vput(vp) 42539397Smckusick register struct vnode *vp; 42639397Smckusick { 42739397Smckusick VOP_UNLOCK(vp); 42839397Smckusick vrele(vp); 42939397Smckusick } 43039397Smckusick 43139397Smckusick /* 43239397Smckusick * Vnode release. 43339397Smckusick * If count drops to zero, call inactive routine and return to freelist. 43439397Smckusick */ 43539397Smckusick void vrele(vp) 43639397Smckusick register struct vnode *vp; 43739397Smckusick { 43839397Smckusick 43939397Smckusick if (vp == NULL) 44039433Smckusick panic("vrele: null vp"); 44139809Smckusick vp->v_usecount--; 44239809Smckusick if (vp->v_usecount < 0) 44339667Smckusick vprint("vrele: bad ref count", vp); 44439809Smckusick if (vp->v_usecount > 0) 44539397Smckusick return; 44639397Smckusick if (vfreeh == (struct vnode *)0) { 44739397Smckusick /* 44839397Smckusick * insert into empty list 44939397Smckusick */ 45039397Smckusick vfreeh = vp; 45139397Smckusick vp->v_freeb = &vfreeh; 45239397Smckusick } else { 45339397Smckusick /* 45439397Smckusick * insert at tail of list 45539397Smckusick */ 45639397Smckusick *vfreet = vp; 45739397Smckusick vp->v_freeb = vfreet; 45839397Smckusick } 45939433Smckusick vp->v_freef = NULL; 46039433Smckusick vfreet = &vp->v_freef; 46139433Smckusick VOP_INACTIVE(vp); 46239397Smckusick } 46339433Smckusick 46439433Smckusick /* 46539809Smckusick * Page or buffer structure gets a reference. 46639809Smckusick */ 46739809Smckusick vhold(vp) 46839809Smckusick register struct vnode *vp; 46939809Smckusick { 47039809Smckusick 47139809Smckusick vp->v_holdcnt++; 47239809Smckusick } 47339809Smckusick 47439809Smckusick /* 47539809Smckusick * Page or buffer structure frees a reference. 47639809Smckusick */ 47739809Smckusick holdrele(vp) 47839809Smckusick register struct vnode *vp; 47939809Smckusick { 48039809Smckusick 48139809Smckusick if (vp->v_holdcnt <= 0) 48239809Smckusick panic("holdrele: holdcnt"); 48339809Smckusick vp->v_holdcnt--; 48439809Smckusick } 48539809Smckusick 48639809Smckusick /* 48739509Smckusick * Remove any vnodes in the vnode table belonging to mount point mp. 48839509Smckusick * 48939509Smckusick * If MNT_NOFORCE is specified, there should not be any active ones, 49039509Smckusick * return error if any are found (nb: this is a user error, not a 49139509Smckusick * system error). If MNT_FORCE is specified, detach any active vnodes 49239509Smckusick * that are found. 49339509Smckusick */ 49439509Smckusick int busyprt = 0; /* patch to print out busy vnodes */ 49539509Smckusick 49639509Smckusick vflush(mp, skipvp, flags) 49739509Smckusick struct mount *mp; 49839509Smckusick struct vnode *skipvp; 49939509Smckusick int flags; 50039509Smckusick { 50139509Smckusick register struct vnode *vp, *nvp; 50239509Smckusick int busy = 0; 50339509Smckusick 50439509Smckusick for (vp = mp->m_mounth; vp; vp = nvp) { 50539509Smckusick nvp = vp->v_mountf; 50639509Smckusick /* 50739509Smckusick * Skip over a selected vnode. 50839509Smckusick * Used by ufs to skip over the quota structure inode. 50939509Smckusick */ 51039509Smckusick if (vp == skipvp) 51139509Smckusick continue; 51239509Smckusick /* 51339809Smckusick * With v_usecount == 0, all we need to do is clear 51439509Smckusick * out the vnode data structures and we are done. 51539509Smckusick */ 51639809Smckusick if (vp->v_usecount == 0) { 51739509Smckusick vgone(vp); 51839509Smckusick continue; 51939509Smckusick } 52039509Smckusick /* 52139509Smckusick * For block or character devices, revert to an 52239509Smckusick * anonymous device. For all other files, just kill them. 52339509Smckusick */ 52439509Smckusick if (flags & MNT_FORCE) { 52539509Smckusick if (vp->v_type != VBLK && vp->v_type != VCHR) { 52639509Smckusick vgone(vp); 52739509Smckusick } else { 52839509Smckusick vclean(vp, 0); 52939509Smckusick vp->v_op = &spec_vnodeops; 53039509Smckusick insmntque(vp, (struct mount *)0); 53139509Smckusick } 53239509Smckusick continue; 53339509Smckusick } 53439509Smckusick if (busyprt) 53539667Smckusick vprint("vflush: busy vnode", vp); 53639509Smckusick busy++; 53739509Smckusick } 53839509Smckusick if (busy) 53939509Smckusick return (EBUSY); 54039509Smckusick return (0); 54139509Smckusick } 54239509Smckusick 54339509Smckusick /* 54439433Smckusick * Disassociate the underlying file system from a vnode. 54539433Smckusick */ 54639484Smckusick void vclean(vp, doclose) 54739433Smckusick register struct vnode *vp; 54839484Smckusick long doclose; 54939433Smckusick { 55039433Smckusick struct vnodeops *origops; 55139484Smckusick int active; 55239433Smckusick 55339484Smckusick /* 55439484Smckusick * Check to see if the vnode is in use. 55539667Smckusick * If so we have to reference it before we clean it out 55639667Smckusick * so that its count cannot fall to zero and generate a 55739667Smckusick * race against ourselves to recycle it. 55839484Smckusick */ 55939809Smckusick if (active = vp->v_usecount) 56039484Smckusick VREF(vp); 56139484Smckusick /* 56239484Smckusick * Prevent the vnode from being recycled or 56339484Smckusick * brought into use while we clean it out. 56439484Smckusick */ 56539667Smckusick if (vp->v_flag & VXLOCK) 56639667Smckusick panic("vclean: deadlock"); 56739433Smckusick vp->v_flag |= VXLOCK; 56839433Smckusick /* 56939667Smckusick * Even if the count is zero, the VOP_INACTIVE routine may still 57039667Smckusick * have the object locked while it cleans it out. The VOP_LOCK 57139667Smckusick * ensures that the VOP_INACTIVE routine is done with its work. 57239667Smckusick * For active vnodes, it ensures that no other activity can 57339667Smckusick * occur while the buffer list is being cleaned out. 57439667Smckusick */ 57539667Smckusick VOP_LOCK(vp); 57639667Smckusick if (doclose) 57739667Smckusick vinvalbuf(vp, 1); 57839667Smckusick /* 57939433Smckusick * Prevent any further operations on the vnode from 58039433Smckusick * being passed through to the old file system. 58139433Smckusick */ 58239433Smckusick origops = vp->v_op; 58339433Smckusick vp->v_op = &dead_vnodeops; 58439433Smckusick vp->v_tag = VT_NON; 58539433Smckusick /* 58639484Smckusick * If purging an active vnode, it must be unlocked, closed, 58739484Smckusick * and deactivated before being reclaimed. 58839433Smckusick */ 58939667Smckusick (*(origops->vn_unlock))(vp); 59039484Smckusick if (active) { 59139484Smckusick if (doclose) 59239484Smckusick (*(origops->vn_close))(vp, 0, NOCRED); 59339433Smckusick (*(origops->vn_inactive))(vp); 59439433Smckusick } 59539433Smckusick /* 59639433Smckusick * Reclaim the vnode. 59739433Smckusick */ 59839433Smckusick if ((*(origops->vn_reclaim))(vp)) 59939433Smckusick panic("vclean: cannot reclaim"); 60039484Smckusick if (active) 60139484Smckusick vrele(vp); 60239433Smckusick /* 60339433Smckusick * Done with purge, notify sleepers in vget of the grim news. 60439433Smckusick */ 60539433Smckusick vp->v_flag &= ~VXLOCK; 60639433Smckusick if (vp->v_flag & VXWANT) { 60739433Smckusick vp->v_flag &= ~VXWANT; 60839433Smckusick wakeup((caddr_t)vp); 60939433Smckusick } 61039433Smckusick } 61139433Smckusick 61239433Smckusick /* 61339633Smckusick * Eliminate all activity associated with the requested vnode 61439633Smckusick * and with all vnodes aliased to the requested vnode. 61539633Smckusick */ 61639633Smckusick void vgoneall(vp) 61739633Smckusick register struct vnode *vp; 61839633Smckusick { 61939809Smckusick register struct vnode *vq; 62039633Smckusick 62139809Smckusick while (vp->v_flag & VALIASED) { 62239809Smckusick for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 62340050Smckusick if (vq->v_rdev != vp->v_rdev || 62440050Smckusick vq->v_type != vp->v_type || vp == vq) 62539633Smckusick continue; 62639633Smckusick vgone(vq); 62739809Smckusick break; 62839633Smckusick } 62939633Smckusick } 63039633Smckusick vgone(vp); 63139633Smckusick } 63239633Smckusick 63339633Smckusick /* 63439433Smckusick * Eliminate all activity associated with a vnode 63539433Smckusick * in preparation for reuse. 63639433Smckusick */ 63739433Smckusick void vgone(vp) 63839433Smckusick register struct vnode *vp; 63939433Smckusick { 64039809Smckusick register struct vnode *vq; 64139615Smckusick struct vnode *vx; 64239615Smckusick long count; 64339433Smckusick 64439433Smckusick /* 64540548Smckusick * If a vgone (or vclean) is already in progress, 64640548Smckusick * wait until it is done and return. 64740548Smckusick */ 64840548Smckusick if (vp->v_flag & VXLOCK) { 64940548Smckusick vp->v_flag |= VXWANT; 65040548Smckusick sleep((caddr_t)vp, PINOD); 65140548Smckusick return; 65240548Smckusick } 65340548Smckusick /* 65439433Smckusick * Clean out the filesystem specific data. 65539433Smckusick */ 65639484Smckusick vclean(vp, 1); 65739433Smckusick /* 65839433Smckusick * Delete from old mount point vnode list, if on one. 65939433Smckusick */ 66039433Smckusick if (vp->v_mountb) { 66139433Smckusick if (vq = vp->v_mountf) 66239433Smckusick vq->v_mountb = vp->v_mountb; 66339433Smckusick *vp->v_mountb = vq; 66439433Smckusick vp->v_mountf = NULL; 66539433Smckusick vp->v_mountb = NULL; 66639433Smckusick } 66739433Smckusick /* 66839433Smckusick * If special device, remove it from special device alias list. 66939433Smckusick */ 67039433Smckusick if (vp->v_type == VBLK || vp->v_type == VCHR) { 67139809Smckusick if (*vp->v_hashchain == vp) { 67239809Smckusick *vp->v_hashchain = vp->v_specnext; 67339433Smckusick } else { 67439809Smckusick for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 67539615Smckusick if (vq->v_specnext != vp) 67639433Smckusick continue; 67739615Smckusick vq->v_specnext = vp->v_specnext; 67839433Smckusick break; 67939433Smckusick } 68039615Smckusick if (vq == NULL) 68139433Smckusick panic("missing bdev"); 68239433Smckusick } 68339615Smckusick if (vp->v_flag & VALIASED) { 68439809Smckusick count = 0; 68539809Smckusick for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 68640108Smckusick if (vq->v_rdev != vp->v_rdev || 68740108Smckusick vq->v_type != vp->v_type) 68839615Smckusick continue; 68939615Smckusick count++; 69039615Smckusick vx = vq; 69139615Smckusick } 69239615Smckusick if (count == 0) 69339615Smckusick panic("missing alias"); 69439615Smckusick if (count == 1) 69539615Smckusick vx->v_flag &= ~VALIASED; 69639615Smckusick vp->v_flag &= ~VALIASED; 69739615Smckusick } 69839615Smckusick FREE(vp->v_specinfo, M_VNODE); 69939615Smckusick vp->v_specinfo = NULL; 70039433Smckusick } 70139433Smckusick /* 70239433Smckusick * If it is on the freelist, move it to the head of the list. 70339433Smckusick */ 70439433Smckusick if (vp->v_freeb) { 70539433Smckusick if (vq = vp->v_freef) 70639433Smckusick vq->v_freeb = vp->v_freeb; 70739433Smckusick else 70839433Smckusick vfreet = vp->v_freeb; 70939433Smckusick *vp->v_freeb = vq; 71039433Smckusick vp->v_freef = vfreeh; 71139433Smckusick vp->v_freeb = &vfreeh; 71239433Smckusick vfreeh->v_freeb = &vp->v_freef; 71339433Smckusick vfreeh = vp; 71439433Smckusick } 71539484Smckusick vp->v_type = VBAD; 71639433Smckusick } 71739633Smckusick 71839633Smckusick /* 71939821Smckusick * Lookup a vnode by device number. 72039821Smckusick */ 72139821Smckusick vfinddev(dev, type, vpp) 72239821Smckusick dev_t dev; 72339821Smckusick enum vtype type; 72439821Smckusick struct vnode **vpp; 72539821Smckusick { 72639821Smckusick register struct vnode *vp; 72739821Smckusick 72839821Smckusick for (vp = speclisth[SPECHASH(dev)]; vp; vp = vp->v_specnext) { 72939821Smckusick if (dev != vp->v_rdev || type != vp->v_type) 73039821Smckusick continue; 73139821Smckusick *vpp = vp; 73239821Smckusick return (0); 73339821Smckusick } 73439821Smckusick return (1); 73539821Smckusick } 73639821Smckusick 73739821Smckusick /* 73839633Smckusick * Calculate the total number of references to a special device. 73939633Smckusick */ 74039633Smckusick vcount(vp) 74139633Smckusick register struct vnode *vp; 74239633Smckusick { 74339809Smckusick register struct vnode *vq; 74439633Smckusick int count; 74539633Smckusick 74639633Smckusick if ((vp->v_flag & VALIASED) == 0) 74739809Smckusick return (vp->v_usecount); 74839633Smckusick loop: 74939809Smckusick for (count = 0, vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { 75040108Smckusick if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) 75139633Smckusick continue; 75239633Smckusick /* 75339633Smckusick * Alias, but not in use, so flush it out. 75439633Smckusick */ 75539809Smckusick if (vq->v_usecount == 0) { 75639633Smckusick vgone(vq); 75739633Smckusick goto loop; 75839633Smckusick } 75939809Smckusick count += vq->v_usecount; 76039633Smckusick } 76139633Smckusick return (count); 76239633Smckusick } 76339667Smckusick 76439667Smckusick /* 76539667Smckusick * Print out a description of a vnode. 76639667Smckusick */ 76739667Smckusick static char *typename[] = 76840286Smckusick { "VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD" }; 76939667Smckusick 77039667Smckusick vprint(label, vp) 77139667Smckusick char *label; 77239667Smckusick register struct vnode *vp; 77339667Smckusick { 77439913Smckusick char buf[64]; 77539667Smckusick 77639667Smckusick if (label != NULL) 77739667Smckusick printf("%s: ", label); 77839913Smckusick printf("type %s, usecount %d, refcount %d,", typename[vp->v_type], 77939809Smckusick vp->v_usecount, vp->v_holdcnt); 78039913Smckusick buf[0] = '\0'; 78139913Smckusick if (vp->v_flag & VROOT) 78239913Smckusick strcat(buf, "|VROOT"); 78339913Smckusick if (vp->v_flag & VTEXT) 78439913Smckusick strcat(buf, "|VTEXT"); 78539913Smckusick if (vp->v_flag & VXLOCK) 78639913Smckusick strcat(buf, "|VXLOCK"); 78739913Smckusick if (vp->v_flag & VXWANT) 78839913Smckusick strcat(buf, "|VXWANT"); 78939913Smckusick if (vp->v_flag & VEXLOCK) 79039913Smckusick strcat(buf, "|VEXLOCK"); 79139913Smckusick if (vp->v_flag & VSHLOCK) 79239913Smckusick strcat(buf, "|VSHLOCK"); 79339913Smckusick if (vp->v_flag & VLWAIT) 79439913Smckusick strcat(buf, "|VLWAIT"); 79539913Smckusick if (vp->v_flag & VALIASED) 79639913Smckusick strcat(buf, "|VALIASED"); 79739913Smckusick if (vp->v_flag & VBWAIT) 79839913Smckusick strcat(buf, "|VBWAIT"); 79939913Smckusick if (buf[0] != '\0') 80039913Smckusick printf(" flags (%s)", &buf[1]); 80139913Smckusick printf("\n\t"); 80239667Smckusick VOP_PRINT(vp); 80339667Smckusick } 804