Index: head/sys/kern/vfs_export.c =================================================================== --- head/sys/kern/vfs_export.c (revision 18972) +++ head/sys/kern/vfs_export.c (revision 18973) @@ -1,1646 +1,1655 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94 - * $Id: vfs_subr.c,v 1.60 1996/09/19 18:20:22 nate Exp $ + * $Id: vfs_subr.c,v 1.61 1996/09/28 03:36:07 dyson Exp $ */ /* * External virtual filesystem routines */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB extern void printlockedvnodes __P((void)); #endif extern void vclean __P((struct vnode *vp, int flags)); extern void vfs_unmountroot __P((struct mount *rootfs)); enum vtype iftovt_tab[16] = { VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, }; int vttoif_tab[9] = { 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, S_IFMT, }; /* * Insq/Remq for the vnode usage lists. */ #define bufinsvn(bp, dp) LIST_INSERT_HEAD(dp, bp, b_vnbufs) #define bufremvn(bp) { \ LIST_REMOVE(bp, b_vnbufs); \ (bp)->b_vnbufs.le_next = NOLIST; \ } TAILQ_HEAD(freelst, vnode) vnode_free_list; /* vnode free list */ static u_long freevnodes = 0; struct mntlist mountlist; /* mounted filesystem list */ int desiredvnodes; SYSCTL_INT(_kern, KERN_MAXVNODES, maxvnodes, CTLFLAG_RD, &desiredvnodes, 0, ""); static void vfs_free_addrlist __P((struct netexport *nep)); static int vfs_free_netcred __P((struct radix_node *rn, void *w)); static int vfs_hang_addrlist __P((struct mount *mp, struct netexport *nep, struct export_args *argp)); /* * Initialize the vnode management data structures. */ void vntblinit() { desiredvnodes = maxproc + vm_object_cache_max + extravnodes; TAILQ_INIT(&vnode_free_list); CIRCLEQ_INIT(&mountlist); } /* * Lock a filesystem. * Used to prevent access to it while mounting and unmounting. */ int vfs_lock(mp) register struct mount *mp; { while (mp->mnt_flag & MNT_MLOCK) { mp->mnt_flag |= MNT_MWAIT; (void) tsleep((caddr_t) mp, PVFS, "vfslck", 0); } mp->mnt_flag |= MNT_MLOCK; return (0); } /* * Unlock a locked filesystem. * Panic if filesystem is not locked. */ void vfs_unlock(mp) register struct mount *mp; { if ((mp->mnt_flag & MNT_MLOCK) == 0) panic("vfs_unlock: not locked"); mp->mnt_flag &= ~MNT_MLOCK; if (mp->mnt_flag & MNT_MWAIT) { mp->mnt_flag &= ~MNT_MWAIT; wakeup((caddr_t) mp); } } /* * Mark a mount point as busy. * Used to synchronize access and to delay unmounting. */ int vfs_busy(mp) register struct mount *mp; { while (mp->mnt_flag & MNT_MPBUSY) { mp->mnt_flag |= MNT_MPWANT; (void) tsleep((caddr_t) &mp->mnt_flag, PVFS, "vfsbsy", 0); } if (mp->mnt_flag & MNT_UNMOUNT) return (1); mp->mnt_flag |= MNT_MPBUSY; return (0); } /* * Free a busy filesystem. * Panic if filesystem is not busy. */ void vfs_unbusy(mp) register struct mount *mp; { if ((mp->mnt_flag & MNT_MPBUSY) == 0) panic("vfs_unbusy: not busy"); mp->mnt_flag &= ~MNT_MPBUSY; if (mp->mnt_flag & MNT_MPWANT) { mp->mnt_flag &= ~MNT_MPWANT; wakeup((caddr_t) &mp->mnt_flag); } } void vfs_unmountroot(struct mount *rootfs) { struct mount *mp = rootfs; int error; if (vfs_busy(mp)) { printf("failed to unmount root\n"); return; } mp->mnt_flag |= MNT_UNMOUNT; if ((error = vfs_lock(mp))) { printf("lock of root filesystem failed (%d)\n", error); return; } vnode_pager_umount(mp); /* release cached vnodes */ cache_purgevfs(mp); /* remove cache entries for this file sys */ if ((error = VFS_SYNC(mp, MNT_WAIT, initproc->p_ucred, initproc))) printf("sync of root filesystem failed (%d)\n", error); if ((error = VFS_UNMOUNT(mp, MNT_FORCE, initproc))) { printf("unmount of root filesystem failed ("); if (error == EBUSY) printf("BUSY)\n"); else printf("%d)\n", error); } mp->mnt_flag &= ~MNT_UNMOUNT; vfs_unbusy(mp); } /* * Unmount all filesystems. Should only be called by halt(). */ void vfs_unmountall() { struct mount *mp, *nmp, *rootfs = NULL; int error; /* unmount all but rootfs */ for (mp = mountlist.cqh_last; mp != (void *)&mountlist; mp = nmp) { nmp = mp->mnt_list.cqe_prev; if (mp->mnt_flag & MNT_ROOTFS) { rootfs = mp; continue; } error = dounmount(mp, MNT_FORCE, initproc); if (error) { printf("unmount of %s failed (", mp->mnt_stat.f_mntonname); if (error == EBUSY) printf("BUSY)\n"); else printf("%d)\n", error); } } /* and finally... */ if (rootfs) { vfs_unmountroot(rootfs); } else { printf("no root filesystem\n"); } } /* * Lookup a mount point by filesystem identifier. */ struct mount * getvfs(fsid) fsid_t *fsid; { register struct mount *mp; for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next) { if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] && mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) return (mp); } return ((struct mount *) 0); } /* * Get a new unique fsid */ void getnewfsid(mp, mtype) struct mount *mp; int mtype; { static u_short xxxfs_mntid; fsid_t tfsid; mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev + mtype, 0); mp->mnt_stat.f_fsid.val[1] = mtype; if (xxxfs_mntid == 0) ++xxxfs_mntid; tfsid.val[0] = makedev(nblkdev + mtype, xxxfs_mntid); tfsid.val[1] = mtype; if (mountlist.cqh_first != (void *)&mountlist) { while (getvfs(&tfsid)) { tfsid.val[0]++; xxxfs_mntid++; } } mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; } /* * Set vnode attributes to VNOVAL */ void vattr_null(vap) register struct vattr *vap; { vap->va_type = VNON; vap->va_size = VNOVAL; vap->va_bytes = VNOVAL; vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid = vap->va_fsid = vap->va_fileid = vap->va_blocksize = vap->va_rdev = vap->va_atime.tv_sec = vap->va_atime.tv_nsec = vap->va_mtime.tv_sec = vap->va_mtime.tv_nsec = vap->va_ctime.tv_sec = vap->va_ctime.tv_nsec = vap->va_flags = vap->va_gen = VNOVAL; vap->va_vaflags = 0; } /* * Routines having to do with the management of the vnode table. */ extern vop_t **dead_vnodeop_p; /* * Return the next vnode from the free list. */ int getnewvnode(tag, mp, vops, vpp) enum vtagtype tag; struct mount *mp; vop_t **vops; struct vnode **vpp; { register struct vnode *vp; retry: vp = vnode_free_list.tqh_first; /* * we allocate a new vnode if * 1. we don't have any free * Pretty obvious, we actually used to panic, but that * is a silly thing to do. * 2. we havn't filled our pool yet * We don't want to trash the incore (VM-)vnodecache. * 3. if less that 1/4th of our vnodes are free. * We don't want to trash the namei cache either. */ if (freevnodes < (numvnodes >> 2) || numvnodes < desiredvnodes || vp == NULL) { vp = (struct vnode *) malloc((u_long) sizeof *vp, M_VNODE, M_WAITOK); bzero((char *) vp, sizeof *vp); numvnodes++; } else { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); if (vp->v_usage > 0) { --vp->v_usage; TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist); goto retry; } freevnodes--; if (vp->v_usecount) panic("free vnode isn't"); /* see comment on why 0xdeadb is set at end of vgone (below) */ vp->v_freelist.tqe_prev = (struct vnode **) 0xdeadb; vp->v_lease = NULL; if (vp->v_type != VBAD) vgone(vp); #ifdef DIAGNOSTIC { int s; if (vp->v_data) panic("cleaned vnode isn't"); s = splbio(); if (vp->v_numoutput) panic("Clean vnode has pending I/O's"); splx(s); } #endif vp->v_flag = 0; vp->v_lastr = 0; vp->v_ralen = 0; vp->v_maxra = 0; vp->v_lastw = 0; vp->v_lasta = 0; vp->v_cstart = 0; vp->v_clen = 0; vp->v_socket = 0; vp->v_writecount = 0; /* XXX */ vp->v_usage = 0; } vp->v_type = VNON; cache_purge(vp); vp->v_tag = tag; vp->v_op = vops; insmntque(vp, mp); *vpp = vp; vp->v_usecount = 1; vp->v_data = 0; return (0); } /* * Move a vnode from one mount queue to another. */ void insmntque(vp, mp) register struct vnode *vp; register struct mount *mp; { /* * Delete from old mount point vnode list, if on one. */ if (vp->v_mount != NULL) LIST_REMOVE(vp, v_mntvnodes); /* * Insert into list of vnodes for the new mount point, if available. */ if ((vp->v_mount = mp) == NULL) return; LIST_INSERT_HEAD(&mp->mnt_vnodelist, vp, v_mntvnodes); } /* * Update outstanding I/O count and do wakeup if requested. */ void vwakeup(bp) register struct buf *bp; { register struct vnode *vp; bp->b_flags &= ~B_WRITEINPROG; if ((vp = bp->b_vp)) { vp->v_numoutput--; if (vp->v_numoutput < 0) panic("vwakeup: neg numoutput"); if ((vp->v_numoutput == 0) && (vp->v_flag & VBWAIT)) { vp->v_flag &= ~VBWAIT; wakeup((caddr_t) &vp->v_numoutput); } } } /* * Flush out and invalidate all buffers associated with a vnode. * Called with the underlying object locked. */ int vinvalbuf(vp, flags, cred, p, slpflag, slptimeo) register struct vnode *vp; int flags; struct ucred *cred; struct proc *p; int slpflag, slptimeo; { register struct buf *bp; struct buf *nbp, *blist; int s, error; vm_object_t object; if (flags & V_SAVE) { if ((error = VOP_FSYNC(vp, cred, MNT_WAIT, p))) return (error); if (vp->v_dirtyblkhd.lh_first != NULL) panic("vinvalbuf: dirty bufs"); } s = splbio(); for (;;) { if ((blist = vp->v_cleanblkhd.lh_first) && (flags & V_SAVEMETA)) while (blist && blist->b_lblkno < 0) blist = blist->b_vnbufs.le_next; if (!blist && (blist = vp->v_dirtyblkhd.lh_first) && (flags & V_SAVEMETA)) while (blist && blist->b_lblkno < 0) blist = blist->b_vnbufs.le_next; if (!blist) break; for (bp = blist; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((flags & V_SAVEMETA) && bp->b_lblkno < 0) continue; if (bp->b_flags & B_BUSY) { bp->b_flags |= B_WANTED; error = tsleep((caddr_t) bp, slpflag | (PRIBIO + 1), "vinvalbuf", slptimeo); splx(s); if (error) return (error); break; } bremfree(bp); bp->b_flags |= B_BUSY; /* * XXX Since there are no node locks for NFS, I * believe there is a slight chance that a delayed * write will occur while sleeping just above, so * check for it. */ if ((bp->b_flags & B_DELWRI) && (flags & V_SAVE)) { (void) VOP_BWRITE(bp); break; } bp->b_flags |= (B_INVAL|B_NOCACHE|B_RELBUF); brelse(bp); } } splx(s); s = splbio(); while (vp->v_numoutput > 0) { vp->v_flag |= VBWAIT; tsleep(&vp->v_numoutput, PVM, "vnvlbv", 0); } splx(s); /* * Destroy the copy in the VM cache, too. */ object = vp->v_object; if (object != NULL) { vm_object_page_remove(object, 0, object->size, (flags & V_SAVE) ? TRUE : FALSE); } if (!(flags & V_SAVEMETA) && (vp->v_dirtyblkhd.lh_first || vp->v_cleanblkhd.lh_first)) panic("vinvalbuf: flush failed"); return (0); } /* * Associate a buffer with a vnode. */ void bgetvp(vp, bp) register struct vnode *vp; register struct buf *bp; { int s; if (bp->b_vp) panic("bgetvp: not free"); VHOLD(vp); bp->b_vp = vp; if (vp->v_type == VBLK || vp->v_type == VCHR) bp->b_dev = vp->v_rdev; else bp->b_dev = NODEV; /* * Insert onto list for new vnode. */ s = splbio(); bufinsvn(bp, &vp->v_cleanblkhd); splx(s); } /* * Disassociate a buffer from a vnode. */ void brelvp(bp) register struct buf *bp; { struct vnode *vp; int s; if (bp->b_vp == (struct vnode *) 0) panic("brelvp: NULL"); /* * Delete from old vnode list, if on one. */ s = splbio(); if (bp->b_vnbufs.le_next != NOLIST) bufremvn(bp); splx(s); vp = bp->b_vp; bp->b_vp = (struct vnode *) 0; HOLDRELE(vp); } /* * Associate a p-buffer with a vnode. */ void pbgetvp(vp, bp) register struct vnode *vp; register struct buf *bp; { if (bp->b_vp) panic("pbgetvp: not free"); VHOLD(vp); bp->b_vp = vp; if (vp->v_type == VBLK || vp->v_type == VCHR) bp->b_dev = vp->v_rdev; else bp->b_dev = NODEV; } /* * Disassociate a p-buffer from a vnode. */ void pbrelvp(bp) register struct buf *bp; { struct vnode *vp; if (bp->b_vp == (struct vnode *) 0) panic("brelvp: NULL"); vp = bp->b_vp; bp->b_vp = (struct vnode *) 0; HOLDRELE(vp); } /* * Reassign a buffer from one vnode to another. * Used to assign file specific control information * (indirect blocks) to the vnode to which they belong. */ void reassignbuf(bp, newvp) register struct buf *bp; register struct vnode *newvp; { int s; if (newvp == NULL) { printf("reassignbuf: NULL"); return; } s = splbio(); /* * Delete from old vnode list, if on one. */ if (bp->b_vnbufs.le_next != NOLIST) bufremvn(bp); /* * If dirty, put on list of dirty buffers; otherwise insert onto list * of clean buffers. */ if (bp->b_flags & B_DELWRI) { struct buf *tbp; tbp = newvp->v_dirtyblkhd.lh_first; if (!tbp || (tbp->b_lblkno > bp->b_lblkno)) { bufinsvn(bp, &newvp->v_dirtyblkhd); } else { while (tbp->b_vnbufs.le_next && (tbp->b_vnbufs.le_next->b_lblkno < bp->b_lblkno)) { tbp = tbp->b_vnbufs.le_next; } LIST_INSERT_AFTER(tbp, bp, b_vnbufs); } } else { bufinsvn(bp, &newvp->v_cleanblkhd); } splx(s); } #ifndef DEVFS_ROOT /* * Create a vnode for a block device. * Used for root filesystem, argdev, and swap areas. * Also used for memory file system special devices. */ int bdevvp(dev, vpp) dev_t dev; struct vnode **vpp; { register struct vnode *vp; struct vnode *nvp; int error; if (dev == NODEV) return (0); error = getnewvnode(VT_NON, (struct mount *) 0, spec_vnodeop_p, &nvp); if (error) { *vpp = 0; return (error); } vp = nvp; vp->v_type = VBLK; if ((nvp = checkalias(vp, dev, (struct mount *) 0))) { vput(vp); vp = nvp; } *vpp = vp; return (0); } #endif /* !DEVFS_ROOT */ /* * Check to see if the new vnode represents a special device * for which we already have a vnode (either because of * bdevvp() or because of a different vnode representing * the same block device). If such an alias exists, deallocate * the existing contents and return the aliased vnode. The * caller is responsible for filling it with its new contents. */ struct vnode * checkalias(nvp, nvp_rdev, mp) register struct vnode *nvp; dev_t nvp_rdev; struct mount *mp; { register struct vnode *vp; struct vnode **vpp; if (nvp->v_type != VBLK && nvp->v_type != VCHR) return (NULLVP); vpp = &speclisth[SPECHASH(nvp_rdev)]; loop: for (vp = *vpp; vp; vp = vp->v_specnext) { if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) continue; /* * Alias, but not in use, so flush it out. */ if (vp->v_usecount == 0) { vgone(vp); goto loop; } if (vget(vp, 1)) goto loop; break; } if (vp == NULL || vp->v_tag != VT_NON) { MALLOC(nvp->v_specinfo, struct specinfo *, sizeof(struct specinfo), M_VNODE, M_WAITOK); nvp->v_rdev = nvp_rdev; nvp->v_hashchain = vpp; nvp->v_specnext = *vpp; nvp->v_specflags = 0; *vpp = nvp; if (vp != NULL) { nvp->v_flag |= VALIASED; vp->v_flag |= VALIASED; vput(vp); } return (NULLVP); } VOP_UNLOCK(vp); vclean(vp, 0); vp->v_op = nvp->v_op; vp->v_tag = nvp->v_tag; nvp->v_type = VNON; insmntque(vp, mp); return (vp); } /* * Grab a particular vnode from the free list, increment its * reference count and lock it. The vnode lock bit is set the * vnode is being eliminated in vgone. The process is awakened * when the transition is completed, and an error returned to * indicate that the vnode is no longer usable (possibly having * been changed to a new file system type). */ int vget(vp, lockflag) register struct vnode *vp; int lockflag; { /* * If the vnode is in the process of being cleaned out for another * use, we wait for the cleaning to finish and then return failure. * Cleaning is determined either by checking that the VXLOCK flag is * set, or that the use count is zero with the back pointer set to * show that it has been removed from the free list by getnewvnode. * The VXLOCK flag may not have been set yet because vclean is blocked * in the VOP_LOCK call waiting for the VOP_INACTIVE to complete. */ if ((vp->v_flag & VXLOCK) || (vp->v_usecount == 0 && vp->v_freelist.tqe_prev == (struct vnode **) 0xdeadb)) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vget", 0); return (1); } if (vp->v_usecount == 0) { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); freevnodes--; } vp->v_usecount++; /* * Create the VM object, if needed */ if ((vp->v_type == VREG) && ((vp->v_object == NULL) || (vp->v_object->flags & OBJ_VFS_REF) == 0)) { vfs_object_create(vp, curproc, curproc->p_ucred, 0); } if (lockflag) VOP_LOCK(vp); return (0); } /* * Vnode reference, just increment the count */ void vref(vp) struct vnode *vp; { if (vp->v_usecount <= 0) panic("vref used where vget required"); vp->v_usecount++; if ((vp->v_type == VREG) && ((vp->v_object == NULL) || ((vp->v_object->flags & OBJ_VFS_REF) == 0)) ) { /* * We need to lock to VP during the time that * the object is created. This is necessary to * keep the system from re-entrantly doing it * multiple times. */ vfs_object_create(vp, curproc, curproc->p_ucred, 0); } } /* * vput(), just unlock and vrele() */ void vput(vp) register struct vnode *vp; { VOP_UNLOCK(vp); vrele(vp); } /* * Vnode release. * If count drops to zero, call inactive routine and return to freelist. */ void vrele(vp) register struct vnode *vp; { #ifdef DIAGNOSTIC if (vp == NULL) panic("vrele: null vp"); #endif vp->v_usecount--; if ((vp->v_usecount == 1) && vp->v_object && (vp->v_object->flags & OBJ_VFS_REF)) { vp->v_object->flags &= ~OBJ_VFS_REF; vm_object_deallocate(vp->v_object); return; } if (vp->v_usecount > 0) return; if (vp->v_usecount < 0) { #ifdef DIAGNOSTIC vprint("vrele: negative ref count", vp); #endif panic("vrele: negative reference cnt"); } if (vp->v_flag & VAGE) { TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist); vp->v_flag &= ~VAGE; vp->v_usage = 0; } else { TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist); } freevnodes++; VOP_INACTIVE(vp); } #ifdef DIAGNOSTIC /* * Page or buffer structure gets a reference. */ void vhold(vp) register struct vnode *vp; { vp->v_holdcnt++; } /* * Page or buffer structure frees a reference. */ void holdrele(vp) register struct vnode *vp; { if (vp->v_holdcnt <= 0) panic("holdrele: holdcnt"); vp->v_holdcnt--; } #endif /* DIAGNOSTIC */ /* * Remove any vnodes in the vnode table belonging to mount point mp. * * If MNT_NOFORCE is specified, there should not be any active ones, * return error if any are found (nb: this is a user error, not a * system error). If MNT_FORCE is specified, detach any active vnodes * that are found. */ #ifdef DIAGNOSTIC static int busyprt = 0; /* print out busy vnodes */ SYSCTL_INT(_debug, 1, busyprt, CTLFLAG_RW, &busyprt, 0, ""); #endif int vflush(mp, skipvp, flags) struct mount *mp; struct vnode *skipvp; int flags; { register struct vnode *vp, *nvp; int busy = 0; if ((mp->mnt_flag & MNT_MPBUSY) == 0) panic("vflush: not busy"); loop: for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) { /* * Make sure this vnode wasn't reclaimed in getnewvnode(). * Start over if it has (it won't be on the list anymore). */ if (vp->v_mount != mp) goto loop; nvp = vp->v_mntvnodes.le_next; /* * Skip over a selected vnode. */ if (vp == skipvp) continue; /* * Skip over a vnodes marked VSYSTEM. */ if ((flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM)) continue; /* * If WRITECLOSE is set, only flush out regular file vnodes * open for writing. */ if ((flags & WRITECLOSE) && (vp->v_writecount == 0 || vp->v_type != VREG)) continue; - if ((vp->v_usecount == 1) && vp->v_object) { + if (vp->v_object && (vp->v_object->flags & OBJ_VFS_REF)) { + vm_object_reference(vp->v_object); pager_cache(vp->v_object, FALSE); + vp->v_object->flags &= ~OBJ_VFS_REF; + vm_object_deallocate(vp->v_object); } /* * With v_usecount == 0, all we need to do is clear out the * vnode data structures and we are done. */ if (vp->v_usecount == 0) { vgone(vp); continue; } + /* * If FORCECLOSE is set, forcibly close the vnode. For block * or character devices, revert to an anonymous device. For * all other files, just kill them. */ if (flags & FORCECLOSE) { if (vp->v_type != VBLK && vp->v_type != VCHR) { vgone(vp); } else { vclean(vp, 0); vp->v_op = spec_vnodeop_p; insmntque(vp, (struct mount *) 0); } continue; } #ifdef DIAGNOSTIC if (busyprt) vprint("vflush: busy vnode", vp); #endif busy++; } if (busy) return (EBUSY); return (0); } /* * Disassociate the underlying file system from a vnode. */ void vclean(struct vnode *vp, int flags) { int active; /* * Check to see if the vnode is in use. If so we have to reference it * before we clean it out so that its count cannot fall to zero and * generate a race against ourselves to recycle it. */ if ((active = vp->v_usecount)) VREF(vp); /* * Even if the count is zero, the VOP_INACTIVE routine may still have * the object locked while it cleans it out. The VOP_LOCK ensures that * the VOP_INACTIVE routine is done with its work. For active vnodes, * it ensures that no other activity can occur while the underlying * object is being cleaned out. */ VOP_LOCK(vp); /* * Prevent the vnode from being recycled or brought into use while we * clean it out. */ if (vp->v_flag & VXLOCK) panic("vclean: deadlock"); vp->v_flag |= VXLOCK; /* * Clean out any buffers associated with the vnode. */ if (flags & DOCLOSE) vinvalbuf(vp, V_SAVE, NOCRED, NULL, 0, 0); /* * Any other processes trying to obtain this lock must first wait for * VXLOCK to clear, then call the new lock operation. */ VOP_UNLOCK(vp); /* * If purging an active vnode, it must be closed and deactivated * before being reclaimed. */ if (active) { if (flags & DOCLOSE) VOP_CLOSE(vp, FNONBLOCK, NOCRED, NULL); VOP_INACTIVE(vp); } /* * Reclaim the vnode. */ if (VOP_RECLAIM(vp)) panic("vclean: cannot reclaim"); if (active) vrele(vp); /* * Done with purge, notify sleepers of the grim news. */ vp->v_op = dead_vnodeop_p; vp->v_tag = VT_NON; vp->v_flag &= ~VXLOCK; if (vp->v_flag & VXWANT) { vp->v_flag &= ~VXWANT; wakeup((caddr_t) vp); } } /* * Eliminate all activity associated with the requested vnode * and with all vnodes aliased to the requested vnode. */ void vgoneall(vp) register struct vnode *vp; { register struct vnode *vq; if (vp->v_flag & VALIASED) { /* * If a vgone (or vclean) is already in progress, wait until * it is done and return. */ if (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vgall", 0); return; } /* * Ensure that vp will not be vgone'd while we are eliminating * its aliases. */ vp->v_flag |= VXLOCK; while (vp->v_flag & VALIASED) { for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type || vp == vq) continue; vgone(vq); break; } } /* * Remove the lock so that vgone below will really eliminate * the vnode after which time vgone will awaken any sleepers. */ vp->v_flag &= ~VXLOCK; } vgone(vp); } /* * Eliminate all activity associated with a vnode * in preparation for reuse. */ void vgone(vp) register struct vnode *vp; { register struct vnode *vq; struct vnode *vx; /* * If a vgone (or vclean) is already in progress, wait until it is * done and return. */ if (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vgone", 0); return; } + + if (vp->v_object) { + vp->v_object->flags |= OBJ_VNODE_GONE; + } + /* * Clean out the filesystem specific data. */ vclean(vp, DOCLOSE); /* * Delete from old mount point vnode list, if on one. */ if (vp->v_mount != NULL) { LIST_REMOVE(vp, v_mntvnodes); vp->v_mount = NULL; } /* * If special device, remove it from special device alias list. */ if (vp->v_type == VBLK || vp->v_type == VCHR) { if (*vp->v_hashchain == vp) { *vp->v_hashchain = vp->v_specnext; } else { for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_specnext != vp) continue; vq->v_specnext = vp->v_specnext; break; } if (vq == NULL) panic("missing bdev"); } if (vp->v_flag & VALIASED) { vx = NULL; for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) continue; if (vx) break; vx = vq; } if (vx == NULL) panic("missing alias"); if (vq == NULL) vx->v_flag &= ~VALIASED; vp->v_flag &= ~VALIASED; } FREE(vp->v_specinfo, M_VNODE); vp->v_specinfo = NULL; } /* * If it is on the freelist and not already at the head, move it to * the head of the list. The test of the back pointer and the * reference count of zero is because it will be removed from the free * list by getnewvnode, but will not have its reference count * incremented until after calling vgone. If the reference count were * incremented first, vgone would (incorrectly) try to close the * previous instance of the underlying object. So, the back pointer is * explicitly set to `0xdeadb' in getnewvnode after removing it from * the freelist to ensure that we do not try to move it here. */ if (vp->v_usecount == 0 && vp->v_freelist.tqe_prev != (struct vnode **) 0xdeadb && vnode_free_list.tqh_first != vp) { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist); } vp->v_type = VBAD; } /* * Lookup a vnode by device number. */ int vfinddev(dev, type, vpp) dev_t dev; enum vtype type; struct vnode **vpp; { register struct vnode *vp; for (vp = speclisth[SPECHASH(dev)]; vp; vp = vp->v_specnext) { if (dev != vp->v_rdev || type != vp->v_type) continue; *vpp = vp; return (1); } return (0); } /* * Calculate the total number of references to a special device. */ int vcount(vp) register struct vnode *vp; { register struct vnode *vq, *vnext; int count; loop: if ((vp->v_flag & VALIASED) == 0) return (vp->v_usecount); for (count = 0, vq = *vp->v_hashchain; vq; vq = vnext) { vnext = vq->v_specnext; if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) continue; /* * Alias, but not in use, so flush it out. */ if (vq->v_usecount == 0 && vq != vp) { vgone(vq); goto loop; } count += vq->v_usecount; } return (count); } /* * Print out a description of a vnode. */ static char *typename[] = {"VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD"}; void vprint(label, vp) char *label; register struct vnode *vp; { char buf[64]; if (label != NULL) printf("%s: ", label); printf("type %s, usecount %d, writecount %d, refcount %ld,", typename[vp->v_type], vp->v_usecount, vp->v_writecount, vp->v_holdcnt); buf[0] = '\0'; if (vp->v_flag & VROOT) strcat(buf, "|VROOT"); if (vp->v_flag & VTEXT) strcat(buf, "|VTEXT"); if (vp->v_flag & VSYSTEM) strcat(buf, "|VSYSTEM"); if (vp->v_flag & VXLOCK) strcat(buf, "|VXLOCK"); if (vp->v_flag & VXWANT) strcat(buf, "|VXWANT"); if (vp->v_flag & VBWAIT) strcat(buf, "|VBWAIT"); if (vp->v_flag & VALIASED) strcat(buf, "|VALIASED"); if (buf[0] != '\0') printf(" flags (%s)", &buf[1]); if (vp->v_data == NULL) { printf("\n"); } else { printf("\n\t"); VOP_PRINT(vp); } } #ifdef DDB /* * List all of the locked vnodes in the system. * Called when debugging the kernel. */ void printlockedvnodes(void) { register struct mount *mp; register struct vnode *vp; printf("Locked vnodes\n"); for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next) { for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) if (VOP_ISLOCKED(vp)) vprint((char *) 0, vp); } } #endif int kinfo_vdebug = 1; int kinfo_vgetfailed; #define KINFO_VNODESLOP 10 /* * Dump vnode list (via sysctl). * Copyout address of vnode followed by vnode. */ /* ARGSUSED */ static int sysctl_vnode SYSCTL_HANDLER_ARGS { register struct mount *mp, *nmp; struct vnode *vp; int error; #define VPTRSZ sizeof (struct vnode *) #define VNODESZ sizeof (struct vnode) req->lock = 0; if (!req->oldptr) /* Make an estimate */ return (SYSCTL_OUT(req, 0, (numvnodes + KINFO_VNODESLOP) * (VPTRSZ + VNODESZ))); for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { nmp = mp->mnt_list.cqe_next; if (vfs_busy(mp)) continue; again: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) { /* * Check that the vp is still associated with this * filesystem. RACE: could have been recycled onto * the same filesystem. */ if (vp->v_mount != mp) { if (kinfo_vdebug) printf("kinfo: vp changed\n"); goto again; } if ((error = SYSCTL_OUT(req, &vp, VPTRSZ)) || (error = SYSCTL_OUT(req, vp, VNODESZ))) { vfs_unbusy(mp); return (error); } } vfs_unbusy(mp); } return (0); } SYSCTL_PROC(_kern, KERN_VNODE, vnode, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_vnode, "S,vnode", ""); /* * Check to see if a filesystem is mounted on a block device. */ int vfs_mountedon(vp) register struct vnode *vp; { register struct vnode *vq; if (vp->v_specflags & SI_MOUNTEDON) return (EBUSY); if (vp->v_flag & VALIASED) { for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) continue; if (vq->v_specflags & SI_MOUNTEDON) return (EBUSY); } } return (0); } /* * Build hash lists of net addresses and hang them off the mount point. * Called by ufs_mount() to set up the lists of export addresses. */ static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, struct export_args *argp) { register struct netcred *np; register struct radix_node_head *rnh; register int i; struct radix_node *rn; struct sockaddr *saddr, *smask = 0; struct domain *dom; int error; if (argp->ex_addrlen == 0) { if (mp->mnt_flag & MNT_DEFEXPORTED) return (EPERM); np = &nep->ne_defexported; np->netc_exflags = argp->ex_flags; np->netc_anon = argp->ex_anon; np->netc_anon.cr_ref = 1; mp->mnt_flag |= MNT_DEFEXPORTED; return (0); } i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK); bzero((caddr_t) np, i); saddr = (struct sockaddr *) (np + 1); if ((error = copyin(argp->ex_addr, (caddr_t) saddr, argp->ex_addrlen))) goto out; if (saddr->sa_len > argp->ex_addrlen) saddr->sa_len = argp->ex_addrlen; if (argp->ex_masklen) { smask = (struct sockaddr *) ((caddr_t) saddr + argp->ex_addrlen); error = copyin(argp->ex_addr, (caddr_t) smask, argp->ex_masklen); if (error) goto out; if (smask->sa_len > argp->ex_masklen) smask->sa_len = argp->ex_masklen; } i = saddr->sa_family; if ((rnh = nep->ne_rtable[i]) == 0) { /* * Seems silly to initialize every AF when most are not used, * do so on demand here */ for (dom = domains; dom; dom = dom->dom_next) if (dom->dom_family == i && dom->dom_rtattach) { dom->dom_rtattach((void **) &nep->ne_rtable[i], dom->dom_rtoffset); break; } if ((rnh = nep->ne_rtable[i]) == 0) { error = ENOBUFS; goto out; } } rn = (*rnh->rnh_addaddr) ((caddr_t) saddr, (caddr_t) smask, rnh, np->netc_rnodes); if (rn == 0 || np != (struct netcred *) rn) { /* already exists */ error = EPERM; goto out; } np->netc_exflags = argp->ex_flags; np->netc_anon = argp->ex_anon; np->netc_anon.cr_ref = 1; return (0); out: free(np, M_NETADDR); return (error); } /* ARGSUSED */ static int vfs_free_netcred(struct radix_node *rn, void *w) { register struct radix_node_head *rnh = (struct radix_node_head *) w; (*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh); free((caddr_t) rn, M_NETADDR); return (0); } /* * Free the net address hash lists that are hanging off the mount points. */ static void vfs_free_addrlist(struct netexport *nep) { register int i; register struct radix_node_head *rnh; for (i = 0; i <= AF_MAX; i++) if ((rnh = nep->ne_rtable[i])) { (*rnh->rnh_walktree) (rnh, vfs_free_netcred, (caddr_t) rnh); free((caddr_t) rnh, M_RTABLE); nep->ne_rtable[i] = 0; } } int vfs_export(mp, nep, argp) struct mount *mp; struct netexport *nep; struct export_args *argp; { int error; if (argp->ex_flags & MNT_DELEXPORT) { vfs_free_addrlist(nep); mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); } if (argp->ex_flags & MNT_EXPORTED) { if ((error = vfs_hang_addrlist(mp, nep, argp))) return (error); mp->mnt_flag |= MNT_EXPORTED; } return (0); } struct netcred * vfs_export_lookup(mp, nep, nam) register struct mount *mp; struct netexport *nep; struct mbuf *nam; { register struct netcred *np; register struct radix_node_head *rnh; struct sockaddr *saddr; np = NULL; if (mp->mnt_flag & MNT_EXPORTED) { /* * Lookup in the export list first. */ if (nam != NULL) { saddr = mtod(nam, struct sockaddr *); rnh = nep->ne_rtable[saddr->sa_family]; if (rnh != NULL) { np = (struct netcred *) (*rnh->rnh_matchaddr) ((caddr_t) saddr, rnh); if (np && np->netc_rnodes->rn_flags & RNF_ROOT) np = NULL; } } /* * If no address match, use the default if it exists. */ if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED) np = &nep->ne_defexported; } return (np); } /* * perform msync on all vnodes under a mount point * the mount point must be locked. */ void vfs_msync(struct mount *mp, int flags) { struct vnode *vp, *nvp; loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { if (vp->v_mount != mp) goto loop; nvp = vp->v_mntvnodes.le_next; if (VOP_ISLOCKED(vp) && (flags != MNT_WAIT)) continue; if (vp->v_object && (vp->v_object->flags & OBJ_MIGHTBEDIRTY)) { vm_object_page_clean(vp->v_object, 0, 0, TRUE, TRUE); } } } /* * Create the VM object needed for VMIO and mmap support. This * is done for all VREG files in the system. Some filesystems might * afford the additional metadata buffering capability of the * VMIO code by making the device node be VMIO mode also. */ int vfs_object_create(vp, p, cred, waslocked) struct vnode *vp; struct proc *p; struct ucred *cred; int waslocked; { struct vattr vat; vm_object_t object; int error = 0; retry: if ((object = vp->v_object) == NULL) { if (vp->v_type == VREG) { if ((error = VOP_GETATTR(vp, &vat, cred, p)) != 0) goto retn; (void) vnode_pager_alloc(vp, OFF_TO_IDX(round_page(vat.va_size)), 0, 0); } else { /* * This simply allocates the biggest object possible * for a VBLK vnode. This should be fixed, but doesn't * cause any problems (yet). */ (void) vnode_pager_alloc(vp, INT_MAX, 0, 0); } vp->v_object->flags |= OBJ_VFS_REF; } else { if (object->flags & OBJ_DEAD) { if (waslocked) VOP_UNLOCK(vp); tsleep(object, PVM, "vodead", 0); if (waslocked) VOP_LOCK(vp); goto retry; } if ((object->flags & OBJ_VFS_REF) == 0) { object->flags |= OBJ_VFS_REF; vm_object_reference(object); } } if (vp->v_object) vp->v_flag |= VVMIO; retn: return error; } Index: head/sys/kern/vfs_subr.c =================================================================== --- head/sys/kern/vfs_subr.c (revision 18972) +++ head/sys/kern/vfs_subr.c (revision 18973) @@ -1,1646 +1,1655 @@ /* * Copyright (c) 1989, 1993 * The Regents of the University of California. All rights reserved. * (c) UNIX System Laboratories, Inc. * All or some portions of this file are derived from material licensed * to the University of California by American Telephone and Telegraph * Co. or Unix System Laboratories, Inc. and are reproduced herein with * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * @(#)vfs_subr.c 8.13 (Berkeley) 4/18/94 - * $Id: vfs_subr.c,v 1.60 1996/09/19 18:20:22 nate Exp $ + * $Id: vfs_subr.c,v 1.61 1996/09/28 03:36:07 dyson Exp $ */ /* * External virtual filesystem routines */ #include "opt_ddb.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DDB extern void printlockedvnodes __P((void)); #endif extern void vclean __P((struct vnode *vp, int flags)); extern void vfs_unmountroot __P((struct mount *rootfs)); enum vtype iftovt_tab[16] = { VNON, VFIFO, VCHR, VNON, VDIR, VNON, VBLK, VNON, VREG, VNON, VLNK, VNON, VSOCK, VNON, VNON, VBAD, }; int vttoif_tab[9] = { 0, S_IFREG, S_IFDIR, S_IFBLK, S_IFCHR, S_IFLNK, S_IFSOCK, S_IFIFO, S_IFMT, }; /* * Insq/Remq for the vnode usage lists. */ #define bufinsvn(bp, dp) LIST_INSERT_HEAD(dp, bp, b_vnbufs) #define bufremvn(bp) { \ LIST_REMOVE(bp, b_vnbufs); \ (bp)->b_vnbufs.le_next = NOLIST; \ } TAILQ_HEAD(freelst, vnode) vnode_free_list; /* vnode free list */ static u_long freevnodes = 0; struct mntlist mountlist; /* mounted filesystem list */ int desiredvnodes; SYSCTL_INT(_kern, KERN_MAXVNODES, maxvnodes, CTLFLAG_RD, &desiredvnodes, 0, ""); static void vfs_free_addrlist __P((struct netexport *nep)); static int vfs_free_netcred __P((struct radix_node *rn, void *w)); static int vfs_hang_addrlist __P((struct mount *mp, struct netexport *nep, struct export_args *argp)); /* * Initialize the vnode management data structures. */ void vntblinit() { desiredvnodes = maxproc + vm_object_cache_max + extravnodes; TAILQ_INIT(&vnode_free_list); CIRCLEQ_INIT(&mountlist); } /* * Lock a filesystem. * Used to prevent access to it while mounting and unmounting. */ int vfs_lock(mp) register struct mount *mp; { while (mp->mnt_flag & MNT_MLOCK) { mp->mnt_flag |= MNT_MWAIT; (void) tsleep((caddr_t) mp, PVFS, "vfslck", 0); } mp->mnt_flag |= MNT_MLOCK; return (0); } /* * Unlock a locked filesystem. * Panic if filesystem is not locked. */ void vfs_unlock(mp) register struct mount *mp; { if ((mp->mnt_flag & MNT_MLOCK) == 0) panic("vfs_unlock: not locked"); mp->mnt_flag &= ~MNT_MLOCK; if (mp->mnt_flag & MNT_MWAIT) { mp->mnt_flag &= ~MNT_MWAIT; wakeup((caddr_t) mp); } } /* * Mark a mount point as busy. * Used to synchronize access and to delay unmounting. */ int vfs_busy(mp) register struct mount *mp; { while (mp->mnt_flag & MNT_MPBUSY) { mp->mnt_flag |= MNT_MPWANT; (void) tsleep((caddr_t) &mp->mnt_flag, PVFS, "vfsbsy", 0); } if (mp->mnt_flag & MNT_UNMOUNT) return (1); mp->mnt_flag |= MNT_MPBUSY; return (0); } /* * Free a busy filesystem. * Panic if filesystem is not busy. */ void vfs_unbusy(mp) register struct mount *mp; { if ((mp->mnt_flag & MNT_MPBUSY) == 0) panic("vfs_unbusy: not busy"); mp->mnt_flag &= ~MNT_MPBUSY; if (mp->mnt_flag & MNT_MPWANT) { mp->mnt_flag &= ~MNT_MPWANT; wakeup((caddr_t) &mp->mnt_flag); } } void vfs_unmountroot(struct mount *rootfs) { struct mount *mp = rootfs; int error; if (vfs_busy(mp)) { printf("failed to unmount root\n"); return; } mp->mnt_flag |= MNT_UNMOUNT; if ((error = vfs_lock(mp))) { printf("lock of root filesystem failed (%d)\n", error); return; } vnode_pager_umount(mp); /* release cached vnodes */ cache_purgevfs(mp); /* remove cache entries for this file sys */ if ((error = VFS_SYNC(mp, MNT_WAIT, initproc->p_ucred, initproc))) printf("sync of root filesystem failed (%d)\n", error); if ((error = VFS_UNMOUNT(mp, MNT_FORCE, initproc))) { printf("unmount of root filesystem failed ("); if (error == EBUSY) printf("BUSY)\n"); else printf("%d)\n", error); } mp->mnt_flag &= ~MNT_UNMOUNT; vfs_unbusy(mp); } /* * Unmount all filesystems. Should only be called by halt(). */ void vfs_unmountall() { struct mount *mp, *nmp, *rootfs = NULL; int error; /* unmount all but rootfs */ for (mp = mountlist.cqh_last; mp != (void *)&mountlist; mp = nmp) { nmp = mp->mnt_list.cqe_prev; if (mp->mnt_flag & MNT_ROOTFS) { rootfs = mp; continue; } error = dounmount(mp, MNT_FORCE, initproc); if (error) { printf("unmount of %s failed (", mp->mnt_stat.f_mntonname); if (error == EBUSY) printf("BUSY)\n"); else printf("%d)\n", error); } } /* and finally... */ if (rootfs) { vfs_unmountroot(rootfs); } else { printf("no root filesystem\n"); } } /* * Lookup a mount point by filesystem identifier. */ struct mount * getvfs(fsid) fsid_t *fsid; { register struct mount *mp; for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next) { if (mp->mnt_stat.f_fsid.val[0] == fsid->val[0] && mp->mnt_stat.f_fsid.val[1] == fsid->val[1]) return (mp); } return ((struct mount *) 0); } /* * Get a new unique fsid */ void getnewfsid(mp, mtype) struct mount *mp; int mtype; { static u_short xxxfs_mntid; fsid_t tfsid; mp->mnt_stat.f_fsid.val[0] = makedev(nblkdev + mtype, 0); mp->mnt_stat.f_fsid.val[1] = mtype; if (xxxfs_mntid == 0) ++xxxfs_mntid; tfsid.val[0] = makedev(nblkdev + mtype, xxxfs_mntid); tfsid.val[1] = mtype; if (mountlist.cqh_first != (void *)&mountlist) { while (getvfs(&tfsid)) { tfsid.val[0]++; xxxfs_mntid++; } } mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; } /* * Set vnode attributes to VNOVAL */ void vattr_null(vap) register struct vattr *vap; { vap->va_type = VNON; vap->va_size = VNOVAL; vap->va_bytes = VNOVAL; vap->va_mode = vap->va_nlink = vap->va_uid = vap->va_gid = vap->va_fsid = vap->va_fileid = vap->va_blocksize = vap->va_rdev = vap->va_atime.tv_sec = vap->va_atime.tv_nsec = vap->va_mtime.tv_sec = vap->va_mtime.tv_nsec = vap->va_ctime.tv_sec = vap->va_ctime.tv_nsec = vap->va_flags = vap->va_gen = VNOVAL; vap->va_vaflags = 0; } /* * Routines having to do with the management of the vnode table. */ extern vop_t **dead_vnodeop_p; /* * Return the next vnode from the free list. */ int getnewvnode(tag, mp, vops, vpp) enum vtagtype tag; struct mount *mp; vop_t **vops; struct vnode **vpp; { register struct vnode *vp; retry: vp = vnode_free_list.tqh_first; /* * we allocate a new vnode if * 1. we don't have any free * Pretty obvious, we actually used to panic, but that * is a silly thing to do. * 2. we havn't filled our pool yet * We don't want to trash the incore (VM-)vnodecache. * 3. if less that 1/4th of our vnodes are free. * We don't want to trash the namei cache either. */ if (freevnodes < (numvnodes >> 2) || numvnodes < desiredvnodes || vp == NULL) { vp = (struct vnode *) malloc((u_long) sizeof *vp, M_VNODE, M_WAITOK); bzero((char *) vp, sizeof *vp); numvnodes++; } else { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); if (vp->v_usage > 0) { --vp->v_usage; TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist); goto retry; } freevnodes--; if (vp->v_usecount) panic("free vnode isn't"); /* see comment on why 0xdeadb is set at end of vgone (below) */ vp->v_freelist.tqe_prev = (struct vnode **) 0xdeadb; vp->v_lease = NULL; if (vp->v_type != VBAD) vgone(vp); #ifdef DIAGNOSTIC { int s; if (vp->v_data) panic("cleaned vnode isn't"); s = splbio(); if (vp->v_numoutput) panic("Clean vnode has pending I/O's"); splx(s); } #endif vp->v_flag = 0; vp->v_lastr = 0; vp->v_ralen = 0; vp->v_maxra = 0; vp->v_lastw = 0; vp->v_lasta = 0; vp->v_cstart = 0; vp->v_clen = 0; vp->v_socket = 0; vp->v_writecount = 0; /* XXX */ vp->v_usage = 0; } vp->v_type = VNON; cache_purge(vp); vp->v_tag = tag; vp->v_op = vops; insmntque(vp, mp); *vpp = vp; vp->v_usecount = 1; vp->v_data = 0; return (0); } /* * Move a vnode from one mount queue to another. */ void insmntque(vp, mp) register struct vnode *vp; register struct mount *mp; { /* * Delete from old mount point vnode list, if on one. */ if (vp->v_mount != NULL) LIST_REMOVE(vp, v_mntvnodes); /* * Insert into list of vnodes for the new mount point, if available. */ if ((vp->v_mount = mp) == NULL) return; LIST_INSERT_HEAD(&mp->mnt_vnodelist, vp, v_mntvnodes); } /* * Update outstanding I/O count and do wakeup if requested. */ void vwakeup(bp) register struct buf *bp; { register struct vnode *vp; bp->b_flags &= ~B_WRITEINPROG; if ((vp = bp->b_vp)) { vp->v_numoutput--; if (vp->v_numoutput < 0) panic("vwakeup: neg numoutput"); if ((vp->v_numoutput == 0) && (vp->v_flag & VBWAIT)) { vp->v_flag &= ~VBWAIT; wakeup((caddr_t) &vp->v_numoutput); } } } /* * Flush out and invalidate all buffers associated with a vnode. * Called with the underlying object locked. */ int vinvalbuf(vp, flags, cred, p, slpflag, slptimeo) register struct vnode *vp; int flags; struct ucred *cred; struct proc *p; int slpflag, slptimeo; { register struct buf *bp; struct buf *nbp, *blist; int s, error; vm_object_t object; if (flags & V_SAVE) { if ((error = VOP_FSYNC(vp, cred, MNT_WAIT, p))) return (error); if (vp->v_dirtyblkhd.lh_first != NULL) panic("vinvalbuf: dirty bufs"); } s = splbio(); for (;;) { if ((blist = vp->v_cleanblkhd.lh_first) && (flags & V_SAVEMETA)) while (blist && blist->b_lblkno < 0) blist = blist->b_vnbufs.le_next; if (!blist && (blist = vp->v_dirtyblkhd.lh_first) && (flags & V_SAVEMETA)) while (blist && blist->b_lblkno < 0) blist = blist->b_vnbufs.le_next; if (!blist) break; for (bp = blist; bp; bp = nbp) { nbp = bp->b_vnbufs.le_next; if ((flags & V_SAVEMETA) && bp->b_lblkno < 0) continue; if (bp->b_flags & B_BUSY) { bp->b_flags |= B_WANTED; error = tsleep((caddr_t) bp, slpflag | (PRIBIO + 1), "vinvalbuf", slptimeo); splx(s); if (error) return (error); break; } bremfree(bp); bp->b_flags |= B_BUSY; /* * XXX Since there are no node locks for NFS, I * believe there is a slight chance that a delayed * write will occur while sleeping just above, so * check for it. */ if ((bp->b_flags & B_DELWRI) && (flags & V_SAVE)) { (void) VOP_BWRITE(bp); break; } bp->b_flags |= (B_INVAL|B_NOCACHE|B_RELBUF); brelse(bp); } } splx(s); s = splbio(); while (vp->v_numoutput > 0) { vp->v_flag |= VBWAIT; tsleep(&vp->v_numoutput, PVM, "vnvlbv", 0); } splx(s); /* * Destroy the copy in the VM cache, too. */ object = vp->v_object; if (object != NULL) { vm_object_page_remove(object, 0, object->size, (flags & V_SAVE) ? TRUE : FALSE); } if (!(flags & V_SAVEMETA) && (vp->v_dirtyblkhd.lh_first || vp->v_cleanblkhd.lh_first)) panic("vinvalbuf: flush failed"); return (0); } /* * Associate a buffer with a vnode. */ void bgetvp(vp, bp) register struct vnode *vp; register struct buf *bp; { int s; if (bp->b_vp) panic("bgetvp: not free"); VHOLD(vp); bp->b_vp = vp; if (vp->v_type == VBLK || vp->v_type == VCHR) bp->b_dev = vp->v_rdev; else bp->b_dev = NODEV; /* * Insert onto list for new vnode. */ s = splbio(); bufinsvn(bp, &vp->v_cleanblkhd); splx(s); } /* * Disassociate a buffer from a vnode. */ void brelvp(bp) register struct buf *bp; { struct vnode *vp; int s; if (bp->b_vp == (struct vnode *) 0) panic("brelvp: NULL"); /* * Delete from old vnode list, if on one. */ s = splbio(); if (bp->b_vnbufs.le_next != NOLIST) bufremvn(bp); splx(s); vp = bp->b_vp; bp->b_vp = (struct vnode *) 0; HOLDRELE(vp); } /* * Associate a p-buffer with a vnode. */ void pbgetvp(vp, bp) register struct vnode *vp; register struct buf *bp; { if (bp->b_vp) panic("pbgetvp: not free"); VHOLD(vp); bp->b_vp = vp; if (vp->v_type == VBLK || vp->v_type == VCHR) bp->b_dev = vp->v_rdev; else bp->b_dev = NODEV; } /* * Disassociate a p-buffer from a vnode. */ void pbrelvp(bp) register struct buf *bp; { struct vnode *vp; if (bp->b_vp == (struct vnode *) 0) panic("brelvp: NULL"); vp = bp->b_vp; bp->b_vp = (struct vnode *) 0; HOLDRELE(vp); } /* * Reassign a buffer from one vnode to another. * Used to assign file specific control information * (indirect blocks) to the vnode to which they belong. */ void reassignbuf(bp, newvp) register struct buf *bp; register struct vnode *newvp; { int s; if (newvp == NULL) { printf("reassignbuf: NULL"); return; } s = splbio(); /* * Delete from old vnode list, if on one. */ if (bp->b_vnbufs.le_next != NOLIST) bufremvn(bp); /* * If dirty, put on list of dirty buffers; otherwise insert onto list * of clean buffers. */ if (bp->b_flags & B_DELWRI) { struct buf *tbp; tbp = newvp->v_dirtyblkhd.lh_first; if (!tbp || (tbp->b_lblkno > bp->b_lblkno)) { bufinsvn(bp, &newvp->v_dirtyblkhd); } else { while (tbp->b_vnbufs.le_next && (tbp->b_vnbufs.le_next->b_lblkno < bp->b_lblkno)) { tbp = tbp->b_vnbufs.le_next; } LIST_INSERT_AFTER(tbp, bp, b_vnbufs); } } else { bufinsvn(bp, &newvp->v_cleanblkhd); } splx(s); } #ifndef DEVFS_ROOT /* * Create a vnode for a block device. * Used for root filesystem, argdev, and swap areas. * Also used for memory file system special devices. */ int bdevvp(dev, vpp) dev_t dev; struct vnode **vpp; { register struct vnode *vp; struct vnode *nvp; int error; if (dev == NODEV) return (0); error = getnewvnode(VT_NON, (struct mount *) 0, spec_vnodeop_p, &nvp); if (error) { *vpp = 0; return (error); } vp = nvp; vp->v_type = VBLK; if ((nvp = checkalias(vp, dev, (struct mount *) 0))) { vput(vp); vp = nvp; } *vpp = vp; return (0); } #endif /* !DEVFS_ROOT */ /* * Check to see if the new vnode represents a special device * for which we already have a vnode (either because of * bdevvp() or because of a different vnode representing * the same block device). If such an alias exists, deallocate * the existing contents and return the aliased vnode. The * caller is responsible for filling it with its new contents. */ struct vnode * checkalias(nvp, nvp_rdev, mp) register struct vnode *nvp; dev_t nvp_rdev; struct mount *mp; { register struct vnode *vp; struct vnode **vpp; if (nvp->v_type != VBLK && nvp->v_type != VCHR) return (NULLVP); vpp = &speclisth[SPECHASH(nvp_rdev)]; loop: for (vp = *vpp; vp; vp = vp->v_specnext) { if (nvp_rdev != vp->v_rdev || nvp->v_type != vp->v_type) continue; /* * Alias, but not in use, so flush it out. */ if (vp->v_usecount == 0) { vgone(vp); goto loop; } if (vget(vp, 1)) goto loop; break; } if (vp == NULL || vp->v_tag != VT_NON) { MALLOC(nvp->v_specinfo, struct specinfo *, sizeof(struct specinfo), M_VNODE, M_WAITOK); nvp->v_rdev = nvp_rdev; nvp->v_hashchain = vpp; nvp->v_specnext = *vpp; nvp->v_specflags = 0; *vpp = nvp; if (vp != NULL) { nvp->v_flag |= VALIASED; vp->v_flag |= VALIASED; vput(vp); } return (NULLVP); } VOP_UNLOCK(vp); vclean(vp, 0); vp->v_op = nvp->v_op; vp->v_tag = nvp->v_tag; nvp->v_type = VNON; insmntque(vp, mp); return (vp); } /* * Grab a particular vnode from the free list, increment its * reference count and lock it. The vnode lock bit is set the * vnode is being eliminated in vgone. The process is awakened * when the transition is completed, and an error returned to * indicate that the vnode is no longer usable (possibly having * been changed to a new file system type). */ int vget(vp, lockflag) register struct vnode *vp; int lockflag; { /* * If the vnode is in the process of being cleaned out for another * use, we wait for the cleaning to finish and then return failure. * Cleaning is determined either by checking that the VXLOCK flag is * set, or that the use count is zero with the back pointer set to * show that it has been removed from the free list by getnewvnode. * The VXLOCK flag may not have been set yet because vclean is blocked * in the VOP_LOCK call waiting for the VOP_INACTIVE to complete. */ if ((vp->v_flag & VXLOCK) || (vp->v_usecount == 0 && vp->v_freelist.tqe_prev == (struct vnode **) 0xdeadb)) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vget", 0); return (1); } if (vp->v_usecount == 0) { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); freevnodes--; } vp->v_usecount++; /* * Create the VM object, if needed */ if ((vp->v_type == VREG) && ((vp->v_object == NULL) || (vp->v_object->flags & OBJ_VFS_REF) == 0)) { vfs_object_create(vp, curproc, curproc->p_ucred, 0); } if (lockflag) VOP_LOCK(vp); return (0); } /* * Vnode reference, just increment the count */ void vref(vp) struct vnode *vp; { if (vp->v_usecount <= 0) panic("vref used where vget required"); vp->v_usecount++; if ((vp->v_type == VREG) && ((vp->v_object == NULL) || ((vp->v_object->flags & OBJ_VFS_REF) == 0)) ) { /* * We need to lock to VP during the time that * the object is created. This is necessary to * keep the system from re-entrantly doing it * multiple times. */ vfs_object_create(vp, curproc, curproc->p_ucred, 0); } } /* * vput(), just unlock and vrele() */ void vput(vp) register struct vnode *vp; { VOP_UNLOCK(vp); vrele(vp); } /* * Vnode release. * If count drops to zero, call inactive routine and return to freelist. */ void vrele(vp) register struct vnode *vp; { #ifdef DIAGNOSTIC if (vp == NULL) panic("vrele: null vp"); #endif vp->v_usecount--; if ((vp->v_usecount == 1) && vp->v_object && (vp->v_object->flags & OBJ_VFS_REF)) { vp->v_object->flags &= ~OBJ_VFS_REF; vm_object_deallocate(vp->v_object); return; } if (vp->v_usecount > 0) return; if (vp->v_usecount < 0) { #ifdef DIAGNOSTIC vprint("vrele: negative ref count", vp); #endif panic("vrele: negative reference cnt"); } if (vp->v_flag & VAGE) { TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist); vp->v_flag &= ~VAGE; vp->v_usage = 0; } else { TAILQ_INSERT_TAIL(&vnode_free_list, vp, v_freelist); } freevnodes++; VOP_INACTIVE(vp); } #ifdef DIAGNOSTIC /* * Page or buffer structure gets a reference. */ void vhold(vp) register struct vnode *vp; { vp->v_holdcnt++; } /* * Page or buffer structure frees a reference. */ void holdrele(vp) register struct vnode *vp; { if (vp->v_holdcnt <= 0) panic("holdrele: holdcnt"); vp->v_holdcnt--; } #endif /* DIAGNOSTIC */ /* * Remove any vnodes in the vnode table belonging to mount point mp. * * If MNT_NOFORCE is specified, there should not be any active ones, * return error if any are found (nb: this is a user error, not a * system error). If MNT_FORCE is specified, detach any active vnodes * that are found. */ #ifdef DIAGNOSTIC static int busyprt = 0; /* print out busy vnodes */ SYSCTL_INT(_debug, 1, busyprt, CTLFLAG_RW, &busyprt, 0, ""); #endif int vflush(mp, skipvp, flags) struct mount *mp; struct vnode *skipvp; int flags; { register struct vnode *vp, *nvp; int busy = 0; if ((mp->mnt_flag & MNT_MPBUSY) == 0) panic("vflush: not busy"); loop: for (vp = mp->mnt_vnodelist.lh_first; vp; vp = nvp) { /* * Make sure this vnode wasn't reclaimed in getnewvnode(). * Start over if it has (it won't be on the list anymore). */ if (vp->v_mount != mp) goto loop; nvp = vp->v_mntvnodes.le_next; /* * Skip over a selected vnode. */ if (vp == skipvp) continue; /* * Skip over a vnodes marked VSYSTEM. */ if ((flags & SKIPSYSTEM) && (vp->v_flag & VSYSTEM)) continue; /* * If WRITECLOSE is set, only flush out regular file vnodes * open for writing. */ if ((flags & WRITECLOSE) && (vp->v_writecount == 0 || vp->v_type != VREG)) continue; - if ((vp->v_usecount == 1) && vp->v_object) { + if (vp->v_object && (vp->v_object->flags & OBJ_VFS_REF)) { + vm_object_reference(vp->v_object); pager_cache(vp->v_object, FALSE); + vp->v_object->flags &= ~OBJ_VFS_REF; + vm_object_deallocate(vp->v_object); } /* * With v_usecount == 0, all we need to do is clear out the * vnode data structures and we are done. */ if (vp->v_usecount == 0) { vgone(vp); continue; } + /* * If FORCECLOSE is set, forcibly close the vnode. For block * or character devices, revert to an anonymous device. For * all other files, just kill them. */ if (flags & FORCECLOSE) { if (vp->v_type != VBLK && vp->v_type != VCHR) { vgone(vp); } else { vclean(vp, 0); vp->v_op = spec_vnodeop_p; insmntque(vp, (struct mount *) 0); } continue; } #ifdef DIAGNOSTIC if (busyprt) vprint("vflush: busy vnode", vp); #endif busy++; } if (busy) return (EBUSY); return (0); } /* * Disassociate the underlying file system from a vnode. */ void vclean(struct vnode *vp, int flags) { int active; /* * Check to see if the vnode is in use. If so we have to reference it * before we clean it out so that its count cannot fall to zero and * generate a race against ourselves to recycle it. */ if ((active = vp->v_usecount)) VREF(vp); /* * Even if the count is zero, the VOP_INACTIVE routine may still have * the object locked while it cleans it out. The VOP_LOCK ensures that * the VOP_INACTIVE routine is done with its work. For active vnodes, * it ensures that no other activity can occur while the underlying * object is being cleaned out. */ VOP_LOCK(vp); /* * Prevent the vnode from being recycled or brought into use while we * clean it out. */ if (vp->v_flag & VXLOCK) panic("vclean: deadlock"); vp->v_flag |= VXLOCK; /* * Clean out any buffers associated with the vnode. */ if (flags & DOCLOSE) vinvalbuf(vp, V_SAVE, NOCRED, NULL, 0, 0); /* * Any other processes trying to obtain this lock must first wait for * VXLOCK to clear, then call the new lock operation. */ VOP_UNLOCK(vp); /* * If purging an active vnode, it must be closed and deactivated * before being reclaimed. */ if (active) { if (flags & DOCLOSE) VOP_CLOSE(vp, FNONBLOCK, NOCRED, NULL); VOP_INACTIVE(vp); } /* * Reclaim the vnode. */ if (VOP_RECLAIM(vp)) panic("vclean: cannot reclaim"); if (active) vrele(vp); /* * Done with purge, notify sleepers of the grim news. */ vp->v_op = dead_vnodeop_p; vp->v_tag = VT_NON; vp->v_flag &= ~VXLOCK; if (vp->v_flag & VXWANT) { vp->v_flag &= ~VXWANT; wakeup((caddr_t) vp); } } /* * Eliminate all activity associated with the requested vnode * and with all vnodes aliased to the requested vnode. */ void vgoneall(vp) register struct vnode *vp; { register struct vnode *vq; if (vp->v_flag & VALIASED) { /* * If a vgone (or vclean) is already in progress, wait until * it is done and return. */ if (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vgall", 0); return; } /* * Ensure that vp will not be vgone'd while we are eliminating * its aliases. */ vp->v_flag |= VXLOCK; while (vp->v_flag & VALIASED) { for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type || vp == vq) continue; vgone(vq); break; } } /* * Remove the lock so that vgone below will really eliminate * the vnode after which time vgone will awaken any sleepers. */ vp->v_flag &= ~VXLOCK; } vgone(vp); } /* * Eliminate all activity associated with a vnode * in preparation for reuse. */ void vgone(vp) register struct vnode *vp; { register struct vnode *vq; struct vnode *vx; /* * If a vgone (or vclean) is already in progress, wait until it is * done and return. */ if (vp->v_flag & VXLOCK) { vp->v_flag |= VXWANT; (void) tsleep((caddr_t) vp, PINOD, "vgone", 0); return; } + + if (vp->v_object) { + vp->v_object->flags |= OBJ_VNODE_GONE; + } + /* * Clean out the filesystem specific data. */ vclean(vp, DOCLOSE); /* * Delete from old mount point vnode list, if on one. */ if (vp->v_mount != NULL) { LIST_REMOVE(vp, v_mntvnodes); vp->v_mount = NULL; } /* * If special device, remove it from special device alias list. */ if (vp->v_type == VBLK || vp->v_type == VCHR) { if (*vp->v_hashchain == vp) { *vp->v_hashchain = vp->v_specnext; } else { for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_specnext != vp) continue; vq->v_specnext = vp->v_specnext; break; } if (vq == NULL) panic("missing bdev"); } if (vp->v_flag & VALIASED) { vx = NULL; for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) continue; if (vx) break; vx = vq; } if (vx == NULL) panic("missing alias"); if (vq == NULL) vx->v_flag &= ~VALIASED; vp->v_flag &= ~VALIASED; } FREE(vp->v_specinfo, M_VNODE); vp->v_specinfo = NULL; } /* * If it is on the freelist and not already at the head, move it to * the head of the list. The test of the back pointer and the * reference count of zero is because it will be removed from the free * list by getnewvnode, but will not have its reference count * incremented until after calling vgone. If the reference count were * incremented first, vgone would (incorrectly) try to close the * previous instance of the underlying object. So, the back pointer is * explicitly set to `0xdeadb' in getnewvnode after removing it from * the freelist to ensure that we do not try to move it here. */ if (vp->v_usecount == 0 && vp->v_freelist.tqe_prev != (struct vnode **) 0xdeadb && vnode_free_list.tqh_first != vp) { TAILQ_REMOVE(&vnode_free_list, vp, v_freelist); TAILQ_INSERT_HEAD(&vnode_free_list, vp, v_freelist); } vp->v_type = VBAD; } /* * Lookup a vnode by device number. */ int vfinddev(dev, type, vpp) dev_t dev; enum vtype type; struct vnode **vpp; { register struct vnode *vp; for (vp = speclisth[SPECHASH(dev)]; vp; vp = vp->v_specnext) { if (dev != vp->v_rdev || type != vp->v_type) continue; *vpp = vp; return (1); } return (0); } /* * Calculate the total number of references to a special device. */ int vcount(vp) register struct vnode *vp; { register struct vnode *vq, *vnext; int count; loop: if ((vp->v_flag & VALIASED) == 0) return (vp->v_usecount); for (count = 0, vq = *vp->v_hashchain; vq; vq = vnext) { vnext = vq->v_specnext; if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) continue; /* * Alias, but not in use, so flush it out. */ if (vq->v_usecount == 0 && vq != vp) { vgone(vq); goto loop; } count += vq->v_usecount; } return (count); } /* * Print out a description of a vnode. */ static char *typename[] = {"VNON", "VREG", "VDIR", "VBLK", "VCHR", "VLNK", "VSOCK", "VFIFO", "VBAD"}; void vprint(label, vp) char *label; register struct vnode *vp; { char buf[64]; if (label != NULL) printf("%s: ", label); printf("type %s, usecount %d, writecount %d, refcount %ld,", typename[vp->v_type], vp->v_usecount, vp->v_writecount, vp->v_holdcnt); buf[0] = '\0'; if (vp->v_flag & VROOT) strcat(buf, "|VROOT"); if (vp->v_flag & VTEXT) strcat(buf, "|VTEXT"); if (vp->v_flag & VSYSTEM) strcat(buf, "|VSYSTEM"); if (vp->v_flag & VXLOCK) strcat(buf, "|VXLOCK"); if (vp->v_flag & VXWANT) strcat(buf, "|VXWANT"); if (vp->v_flag & VBWAIT) strcat(buf, "|VBWAIT"); if (vp->v_flag & VALIASED) strcat(buf, "|VALIASED"); if (buf[0] != '\0') printf(" flags (%s)", &buf[1]); if (vp->v_data == NULL) { printf("\n"); } else { printf("\n\t"); VOP_PRINT(vp); } } #ifdef DDB /* * List all of the locked vnodes in the system. * Called when debugging the kernel. */ void printlockedvnodes(void) { register struct mount *mp; register struct vnode *vp; printf("Locked vnodes\n"); for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = mp->mnt_list.cqe_next) { for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) if (VOP_ISLOCKED(vp)) vprint((char *) 0, vp); } } #endif int kinfo_vdebug = 1; int kinfo_vgetfailed; #define KINFO_VNODESLOP 10 /* * Dump vnode list (via sysctl). * Copyout address of vnode followed by vnode. */ /* ARGSUSED */ static int sysctl_vnode SYSCTL_HANDLER_ARGS { register struct mount *mp, *nmp; struct vnode *vp; int error; #define VPTRSZ sizeof (struct vnode *) #define VNODESZ sizeof (struct vnode) req->lock = 0; if (!req->oldptr) /* Make an estimate */ return (SYSCTL_OUT(req, 0, (numvnodes + KINFO_VNODESLOP) * (VPTRSZ + VNODESZ))); for (mp = mountlist.cqh_first; mp != (void *)&mountlist; mp = nmp) { nmp = mp->mnt_list.cqe_next; if (vfs_busy(mp)) continue; again: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = vp->v_mntvnodes.le_next) { /* * Check that the vp is still associated with this * filesystem. RACE: could have been recycled onto * the same filesystem. */ if (vp->v_mount != mp) { if (kinfo_vdebug) printf("kinfo: vp changed\n"); goto again; } if ((error = SYSCTL_OUT(req, &vp, VPTRSZ)) || (error = SYSCTL_OUT(req, vp, VNODESZ))) { vfs_unbusy(mp); return (error); } } vfs_unbusy(mp); } return (0); } SYSCTL_PROC(_kern, KERN_VNODE, vnode, CTLTYPE_OPAQUE|CTLFLAG_RD, 0, 0, sysctl_vnode, "S,vnode", ""); /* * Check to see if a filesystem is mounted on a block device. */ int vfs_mountedon(vp) register struct vnode *vp; { register struct vnode *vq; if (vp->v_specflags & SI_MOUNTEDON) return (EBUSY); if (vp->v_flag & VALIASED) { for (vq = *vp->v_hashchain; vq; vq = vq->v_specnext) { if (vq->v_rdev != vp->v_rdev || vq->v_type != vp->v_type) continue; if (vq->v_specflags & SI_MOUNTEDON) return (EBUSY); } } return (0); } /* * Build hash lists of net addresses and hang them off the mount point. * Called by ufs_mount() to set up the lists of export addresses. */ static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, struct export_args *argp) { register struct netcred *np; register struct radix_node_head *rnh; register int i; struct radix_node *rn; struct sockaddr *saddr, *smask = 0; struct domain *dom; int error; if (argp->ex_addrlen == 0) { if (mp->mnt_flag & MNT_DEFEXPORTED) return (EPERM); np = &nep->ne_defexported; np->netc_exflags = argp->ex_flags; np->netc_anon = argp->ex_anon; np->netc_anon.cr_ref = 1; mp->mnt_flag |= MNT_DEFEXPORTED; return (0); } i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK); bzero((caddr_t) np, i); saddr = (struct sockaddr *) (np + 1); if ((error = copyin(argp->ex_addr, (caddr_t) saddr, argp->ex_addrlen))) goto out; if (saddr->sa_len > argp->ex_addrlen) saddr->sa_len = argp->ex_addrlen; if (argp->ex_masklen) { smask = (struct sockaddr *) ((caddr_t) saddr + argp->ex_addrlen); error = copyin(argp->ex_addr, (caddr_t) smask, argp->ex_masklen); if (error) goto out; if (smask->sa_len > argp->ex_masklen) smask->sa_len = argp->ex_masklen; } i = saddr->sa_family; if ((rnh = nep->ne_rtable[i]) == 0) { /* * Seems silly to initialize every AF when most are not used, * do so on demand here */ for (dom = domains; dom; dom = dom->dom_next) if (dom->dom_family == i && dom->dom_rtattach) { dom->dom_rtattach((void **) &nep->ne_rtable[i], dom->dom_rtoffset); break; } if ((rnh = nep->ne_rtable[i]) == 0) { error = ENOBUFS; goto out; } } rn = (*rnh->rnh_addaddr) ((caddr_t) saddr, (caddr_t) smask, rnh, np->netc_rnodes); if (rn == 0 || np != (struct netcred *) rn) { /* already exists */ error = EPERM; goto out; } np->netc_exflags = argp->ex_flags; np->netc_anon = argp->ex_anon; np->netc_anon.cr_ref = 1; return (0); out: free(np, M_NETADDR); return (error); } /* ARGSUSED */ static int vfs_free_netcred(struct radix_node *rn, void *w) { register struct radix_node_head *rnh = (struct radix_node_head *) w; (*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh); free((caddr_t) rn, M_NETADDR); return (0); } /* * Free the net address hash lists that are hanging off the mount points. */ static void vfs_free_addrlist(struct netexport *nep) { register int i; register struct radix_node_head *rnh; for (i = 0; i <= AF_MAX; i++) if ((rnh = nep->ne_rtable[i])) { (*rnh->rnh_walktree) (rnh, vfs_free_netcred, (caddr_t) rnh); free((caddr_t) rnh, M_RTABLE); nep->ne_rtable[i] = 0; } } int vfs_export(mp, nep, argp) struct mount *mp; struct netexport *nep; struct export_args *argp; { int error; if (argp->ex_flags & MNT_DELEXPORT) { vfs_free_addrlist(nep); mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); } if (argp->ex_flags & MNT_EXPORTED) { if ((error = vfs_hang_addrlist(mp, nep, argp))) return (error); mp->mnt_flag |= MNT_EXPORTED; } return (0); } struct netcred * vfs_export_lookup(mp, nep, nam) register struct mount *mp; struct netexport *nep; struct mbuf *nam; { register struct netcred *np; register struct radix_node_head *rnh; struct sockaddr *saddr; np = NULL; if (mp->mnt_flag & MNT_EXPORTED) { /* * Lookup in the export list first. */ if (nam != NULL) { saddr = mtod(nam, struct sockaddr *); rnh = nep->ne_rtable[saddr->sa_family]; if (rnh != NULL) { np = (struct netcred *) (*rnh->rnh_matchaddr) ((caddr_t) saddr, rnh); if (np && np->netc_rnodes->rn_flags & RNF_ROOT) np = NULL; } } /* * If no address match, use the default if it exists. */ if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED) np = &nep->ne_defexported; } return (np); } /* * perform msync on all vnodes under a mount point * the mount point must be locked. */ void vfs_msync(struct mount *mp, int flags) { struct vnode *vp, *nvp; loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { if (vp->v_mount != mp) goto loop; nvp = vp->v_mntvnodes.le_next; if (VOP_ISLOCKED(vp) && (flags != MNT_WAIT)) continue; if (vp->v_object && (vp->v_object->flags & OBJ_MIGHTBEDIRTY)) { vm_object_page_clean(vp->v_object, 0, 0, TRUE, TRUE); } } } /* * Create the VM object needed for VMIO and mmap support. This * is done for all VREG files in the system. Some filesystems might * afford the additional metadata buffering capability of the * VMIO code by making the device node be VMIO mode also. */ int vfs_object_create(vp, p, cred, waslocked) struct vnode *vp; struct proc *p; struct ucred *cred; int waslocked; { struct vattr vat; vm_object_t object; int error = 0; retry: if ((object = vp->v_object) == NULL) { if (vp->v_type == VREG) { if ((error = VOP_GETATTR(vp, &vat, cred, p)) != 0) goto retn; (void) vnode_pager_alloc(vp, OFF_TO_IDX(round_page(vat.va_size)), 0, 0); } else { /* * This simply allocates the biggest object possible * for a VBLK vnode. This should be fixed, but doesn't * cause any problems (yet). */ (void) vnode_pager_alloc(vp, INT_MAX, 0, 0); } vp->v_object->flags |= OBJ_VFS_REF; } else { if (object->flags & OBJ_DEAD) { if (waslocked) VOP_UNLOCK(vp); tsleep(object, PVM, "vodead", 0); if (waslocked) VOP_LOCK(vp); goto retry; } if ((object->flags & OBJ_VFS_REF) == 0) { object->flags |= OBJ_VFS_REF; vm_object_reference(object); } } if (vp->v_object) vp->v_flag |= VVMIO; retn: return error; } Index: head/sys/vm/vm_object.h =================================================================== --- head/sys/vm/vm_object.h (revision 18972) +++ head/sys/vm/vm_object.h (revision 18973) @@ -1,188 +1,189 @@ /* * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * The Mach Operating System project at Carnegie-Mellon University. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)vm_object.h 8.3 (Berkeley) 1/12/94 * * * Copyright (c) 1987, 1990 Carnegie-Mellon University. * All rights reserved. * * Authors: Avadis Tevanian, Jr., Michael Wayne Young * * Permission to use, copy, modify and distribute this software and * its documentation is hereby granted, provided that both the copyright * notice and this permission notice appear in all copies of the * software, derivative works or modified versions, and any portions * thereof, and that both notices appear in supporting documentation. * * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. * * Carnegie Mellon requests users of this software to return to * * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU * School of Computer Science * Carnegie Mellon University * Pittsburgh PA 15213-3890 * * any improvements or extensions that they make and grant Carnegie the * rights to redistribute these changes. * - * $Id: vm_object.h,v 1.31 1996/09/28 03:33:31 dyson Exp $ + * $Id: vm_object.h,v 1.32 1996/10/15 18:23:38 bde Exp $ */ /* * Virtual memory object module definitions. */ #ifndef _VM_OBJECT_ #define _VM_OBJECT_ #include enum obj_type { OBJT_DEFAULT, OBJT_SWAP, OBJT_VNODE, OBJT_DEVICE }; typedef enum obj_type objtype_t; /* * Types defined: * * vm_object_t Virtual memory object. */ struct vm_object { TAILQ_ENTRY(vm_object) object_list; /* list of all objects */ TAILQ_ENTRY(vm_object) cached_list; /* list of cached (persistent) objects */ TAILQ_HEAD(, vm_object) shadow_head; /* objects that this is a shadow for */ TAILQ_ENTRY(vm_object) shadow_list; /* chain of shadow objects */ TAILQ_HEAD(, vm_page) memq; /* list of resident pages */ objtype_t type; /* type of pager */ vm_size_t size; /* Object size */ int ref_count; /* How many refs?? */ int shadow_count; /* how many objects that this is a shadow for */ int pg_color; /* color of first page in obj */ u_short flags; /* see below */ u_short paging_in_progress; /* Paging (in or out) so don't collapse or destroy */ u_short behavior; /* see below */ int resident_page_count; /* number of resident pages */ vm_ooffset_t paging_offset; /* Offset into paging space */ struct vm_object *backing_object; /* object that I'm a shadow of */ vm_ooffset_t backing_object_offset;/* Offset in backing object */ vm_offset_t last_read; /* last read in object -- detect seq behavior */ vm_page_t page_hint; /* hint for last looked-up or allocated page */ TAILQ_ENTRY(vm_object) pager_object_list; /* list of all objects of this pager type */ void *handle; union { struct { off_t vnp_size; /* Current size of file */ } vnp; struct { TAILQ_HEAD(, vm_page) devp_pglist; /* list of pages allocated */ } devp; struct { int swp_nblocks; int swp_allocsize; struct swblock *swp_blocks; short swp_poip; } swp; } un_pager; }; /* * Flags */ #define OBJ_CANPERSIST 0x0001 /* allow to persist */ #define OBJ_ACTIVE 0x0004 /* active objects */ #define OBJ_DEAD 0x0008 /* dead objects (during rundown) */ #define OBJ_PIPWNT 0x0040 /* paging in progress wanted */ #define OBJ_WRITEABLE 0x0080 /* object has been made writable */ #define OBJ_MIGHTBEDIRTY 0x0100 /* object might be dirty */ #define OBJ_CLEANING 0x0200 #define OBJ_VFS_REF 0x0400 /* object is refed by vfs layer */ +#define OBJ_VNODE_GONE 0x0800 /* vnode is gone */ #define OBJ_NORMAL 0x0 /* default behavior */ #define OBJ_SEQUENTIAL 0x1 /* expect sequential accesses */ #define OBJ_RANDOM 0x2 /* expect random accesses */ #define IDX_TO_OFF(idx) (((vm_ooffset_t)(idx)) << PAGE_SHIFT) #define OFF_TO_IDX(off) ((vm_pindex_t)(((vm_ooffset_t)(off)) >> PAGE_SHIFT)) #ifdef KERNEL extern int vm_object_cache_max; TAILQ_HEAD(object_q, vm_object); extern struct object_q vm_object_cached_list; /* list of objects persisting */ extern int vm_object_cached; /* size of cached list */ extern struct object_q vm_object_list; /* list of allocated objects */ extern long vm_object_count; /* count of all objects */ /* lock for object list and count */ extern vm_object_t kernel_object; /* the single kernel object */ extern vm_object_t kmem_object; #endif /* KERNEL */ #ifdef KERNEL static __inline void vm_object_pip_wakeup(vm_object_t object) { object->paging_in_progress--; if ((object->flags & OBJ_PIPWNT) && object->paging_in_progress == 0) { object->flags &= ~OBJ_PIPWNT; wakeup(object); } } vm_object_t vm_object_allocate __P((objtype_t, vm_size_t)); void vm_object_cache_clear __P((void)); boolean_t vm_object_coalesce __P((vm_object_t, vm_pindex_t, vm_size_t, vm_size_t)); void vm_object_collapse __P((vm_object_t)); void vm_object_copy __P((vm_object_t, vm_pindex_t, vm_object_t *, vm_pindex_t *, boolean_t *)); void vm_object_deallocate __P((vm_object_t)); void vm_object_init __P((void)); void vm_object_page_clean __P((vm_object_t, vm_pindex_t, vm_pindex_t, boolean_t, boolean_t)); void vm_object_page_remove __P((vm_object_t, vm_pindex_t, vm_pindex_t, boolean_t)); void vm_object_pmap_copy __P((vm_object_t, vm_pindex_t, vm_pindex_t)); void vm_object_pmap_remove __P((vm_object_t, vm_pindex_t, vm_pindex_t)); void vm_object_reference __P((vm_object_t)); void vm_object_shadow __P((vm_object_t *, vm_ooffset_t *, vm_size_t)); void vm_object_madvise __P((vm_object_t, vm_pindex_t, int, int)); #endif /* KERNEL */ #endif /* _VM_OBJECT_ */ Index: head/sys/vm/vnode_pager.c =================================================================== --- head/sys/vm/vnode_pager.c (revision 18972) +++ head/sys/vm/vnode_pager.c (revision 18973) @@ -1,971 +1,977 @@ /* * Copyright (c) 1990 University of Utah. * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * Copyright (c) 1993, 1994 John S. Dyson * Copyright (c) 1995, David Greenman * * This code is derived from software contributed to Berkeley by * the Systems Programming Group of the University of Utah Computer * Science Department. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)vnode_pager.c 7.5 (Berkeley) 4/20/91 - * $Id: vnode_pager.c,v 1.63 1996/08/21 21:56:23 dyson Exp $ + * $Id: vnode_pager.c,v 1.64 1996/09/10 05:28:23 dyson Exp $ */ /* * Page to/from files (vnodes). */ /* * TODO: * Implement VOP_GETPAGES/PUTPAGES interface for filesystems. Will * greatly re-simplify the vnode_pager. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static vm_offset_t vnode_pager_addr __P((struct vnode *vp, vm_ooffset_t address, int *run)); static void vnode_pager_iodone __P((struct buf *bp)); static int vnode_pager_input_smlfs __P((vm_object_t object, vm_page_t m)); static int vnode_pager_input_old __P((vm_object_t object, vm_page_t m)); static void vnode_pager_dealloc __P((vm_object_t)); static int vnode_pager_getpages __P((vm_object_t, vm_page_t *, int, int)); static int vnode_pager_putpages __P((vm_object_t, vm_page_t *, int, boolean_t, int *)); static boolean_t vnode_pager_haspage __P((vm_object_t, vm_pindex_t, int *, int *)); struct pagerops vnodepagerops = { NULL, vnode_pager_alloc, vnode_pager_dealloc, vnode_pager_getpages, vnode_pager_putpages, vnode_pager_haspage, NULL }; static int vnode_pager_leaf_getpages __P((vm_object_t object, vm_page_t *m, int count, int reqpage)); static int vnode_pager_leaf_putpages __P((vm_object_t object, vm_page_t *m, int count, boolean_t sync, int *rtvals)); /* * Allocate (or lookup) pager for a vnode. * Handle is a vnode pointer. */ vm_object_t vnode_pager_alloc(handle, size, prot, offset) void *handle; vm_size_t size; vm_prot_t prot; vm_ooffset_t offset; { vm_object_t object; struct vnode *vp; /* * Pageout to vnode, no can do yet. */ if (handle == NULL) return (NULL); vp = (struct vnode *) handle; /* * Prevent race condition when allocating the object. This * can happen with NFS vnodes since the nfsnode isn't locked. */ while (vp->v_flag & VOLOCK) { vp->v_flag |= VOWANT; tsleep(vp, PVM, "vnpobj", 0); } vp->v_flag |= VOLOCK; /* * If the object is being terminated, wait for it to * go away. */ while (((object = vp->v_object) != NULL) && (object->flags & OBJ_DEAD)) { tsleep(object, PVM, "vadead", 0); } if (object == NULL) { /* * And an object of the appropriate size */ object = vm_object_allocate(OBJT_VNODE, size); if (vp->v_type == VREG) object->flags = OBJ_CANPERSIST; else object->flags = 0; /* * Hold a reference to the vnode and initialize object data. */ vp->v_usecount++; object->un_pager.vnp.vnp_size = (vm_ooffset_t) size * PAGE_SIZE; object->handle = handle; vp->v_object = object; } else { /* * vm_object_reference() will remove the object from the cache if * found and gain a reference to the object. */ vm_object_reference(object); } if (vp->v_type == VREG) vp->v_flag |= VVMIO; vp->v_flag &= ~VOLOCK; if (vp->v_flag & VOWANT) { vp->v_flag &= ~VOWANT; wakeup(vp); } return (object); } static void vnode_pager_dealloc(object) vm_object_t object; { register struct vnode *vp = object->handle; if (vp == NULL) panic("vnode_pager_dealloc: pager already dealloced"); if (object->paging_in_progress) { int s = splbio(); while (object->paging_in_progress) { object->flags |= OBJ_PIPWNT; tsleep(object, PVM, "vnpdea", 0); } splx(s); } object->handle = NULL; vp->v_object = NULL; vp->v_flag &= ~(VTEXT | VVMIO); vp->v_flag |= VAGE; vrele(vp); } static boolean_t vnode_pager_haspage(object, pindex, before, after) vm_object_t object; vm_pindex_t pindex; int *before; int *after; { struct vnode *vp = object->handle; daddr_t bn; int err; daddr_t reqblock; int poff; int bsize; int pagesperblock, blocksperpage; /* * If filesystem no longer mounted or offset beyond end of file we do * not have the page. */ if ((vp->v_mount == NULL) || (IDX_TO_OFF(pindex) >= object->un_pager.vnp.vnp_size)) return FALSE; bsize = vp->v_mount->mnt_stat.f_iosize; pagesperblock = bsize / PAGE_SIZE; blocksperpage = 0; if (pagesperblock > 0) { reqblock = pindex / pagesperblock; } else { blocksperpage = (PAGE_SIZE / bsize); reqblock = pindex * blocksperpage; } err = VOP_BMAP(vp, reqblock, (struct vnode **) 0, &bn, after, before); if (err) return TRUE; if ( bn == -1) return FALSE; if (pagesperblock > 0) { poff = pindex - (reqblock * pagesperblock); if (before) { *before *= pagesperblock; *before += poff; } if (after) { int numafter; *after *= pagesperblock; numafter = pagesperblock - (poff + 1); if (IDX_TO_OFF(pindex + numafter) > object->un_pager.vnp.vnp_size) { numafter = OFF_TO_IDX((object->un_pager.vnp.vnp_size - IDX_TO_OFF(pindex))); } *after += numafter; } } else { if (before) { *before /= blocksperpage; } if (after) { *after /= blocksperpage; } } return TRUE; } /* * Lets the VM system know about a change in size for a file. * We adjust our own internal size and flush any cached pages in * the associated object that are affected by the size change. * * Note: this routine may be invoked as a result of a pager put * operation (possibly at object termination time), so we must be careful. */ void vnode_pager_setsize(vp, nsize) struct vnode *vp; vm_ooffset_t nsize; { vm_object_t object = vp->v_object; if (object == NULL) return; /* * Hasn't changed size */ if (nsize == object->un_pager.vnp.vnp_size) return; /* * File has shrunk. Toss any cached pages beyond the new EOF. */ if (nsize < object->un_pager.vnp.vnp_size) { vm_ooffset_t nsizerounded; nsizerounded = IDX_TO_OFF(OFF_TO_IDX(nsize + PAGE_MASK)); if (nsizerounded < object->un_pager.vnp.vnp_size) { vm_object_page_remove(object, OFF_TO_IDX(nsize + PAGE_MASK), OFF_TO_IDX(object->un_pager.vnp.vnp_size), FALSE); } /* * this gets rid of garbage at the end of a page that is now * only partially backed by the vnode... */ if (nsize & PAGE_MASK) { vm_offset_t kva; vm_page_t m; m = vm_page_lookup(object, OFF_TO_IDX(nsize)); if (m) { kva = vm_pager_map_page(m); bzero((caddr_t) kva + (nsize & PAGE_MASK), (int) (round_page(nsize) - nsize)); vm_pager_unmap_page(kva); } } } object->un_pager.vnp.vnp_size = nsize; object->size = OFF_TO_IDX(nsize + PAGE_MASK); } void vnode_pager_umount(mp) register struct mount *mp; { struct vnode *vp, *nvp; loop: for (vp = mp->mnt_vnodelist.lh_first; vp != NULL; vp = nvp) { /* * Vnode can be reclaimed by getnewvnode() while we * traverse the list. */ if (vp->v_mount != mp) goto loop; /* * Save the next pointer now since uncaching may terminate the * object and render vnode invalid */ nvp = vp->v_mntvnodes.le_next; if (vp->v_object != NULL) { VOP_LOCK(vp); vnode_pager_uncache(vp); VOP_UNLOCK(vp); } } } /* * Remove vnode associated object from the object cache. * This routine must be called with the vnode locked. * * XXX unlock the vnode. * We must do this since uncaching the object may result in its * destruction which may initiate paging activity which may necessitate * re-locking the vnode. */ void vnode_pager_uncache(vp) struct vnode *vp; { vm_object_t object; /* * Not a mapped vnode */ object = vp->v_object; if (object == NULL) return; vm_object_reference(object); /* * XXX We really should handle locking on * VBLK devices... */ if (vp->v_type != VBLK) VOP_UNLOCK(vp); pager_cache(object, FALSE); if (vp->v_type != VBLK) VOP_LOCK(vp); return; } void vnode_pager_freepage(m) vm_page_t m; { PAGE_WAKEUP(m); vm_page_free(m); } /* * calculate the linear (byte) disk address of specified virtual * file address */ static vm_offset_t vnode_pager_addr(vp, address, run) struct vnode *vp; vm_ooffset_t address; int *run; { int rtaddress; int bsize; daddr_t block; struct vnode *rtvp; int err; daddr_t vblock; int voffset; if ((int) address < 0) return -1; if (vp->v_mount == NULL) return -1; bsize = vp->v_mount->mnt_stat.f_iosize; vblock = address / bsize; voffset = address % bsize; err = VOP_BMAP(vp, vblock, &rtvp, &block, run, NULL); if (err || (block == -1)) rtaddress = -1; else { rtaddress = block + voffset / DEV_BSIZE; if( run) { *run += 1; *run *= bsize/PAGE_SIZE; *run -= voffset/PAGE_SIZE; } } return rtaddress; } /* * interrupt routine for I/O completion */ static void vnode_pager_iodone(bp) struct buf *bp; { bp->b_flags |= B_DONE; wakeup(bp); } /* * small block file system vnode pager input */ static int vnode_pager_input_smlfs(object, m) vm_object_t object; vm_page_t m; { int i; int s; struct vnode *dp, *vp; struct buf *bp; vm_offset_t kva; int fileaddr; vm_offset_t bsize; int error = 0; vp = object->handle; if (vp->v_mount == NULL) return VM_PAGER_BAD; bsize = vp->v_mount->mnt_stat.f_iosize; VOP_BMAP(vp, 0, &dp, 0, NULL, NULL); kva = vm_pager_map_page(m); for (i = 0; i < PAGE_SIZE / bsize; i++) { if ((vm_page_bits(IDX_TO_OFF(m->pindex) + i * bsize, bsize) & m->valid)) continue; fileaddr = vnode_pager_addr(vp, IDX_TO_OFF(m->pindex) + i * bsize, (int *)0); if (fileaddr != -1) { bp = getpbuf(); /* build a minimal buffer header */ bp->b_flags = B_BUSY | B_READ | B_CALL; bp->b_iodone = vnode_pager_iodone; bp->b_proc = curproc; bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred; if (bp->b_rcred != NOCRED) crhold(bp->b_rcred); if (bp->b_wcred != NOCRED) crhold(bp->b_wcred); bp->b_un.b_addr = (caddr_t) kva + i * bsize; bp->b_blkno = fileaddr; pbgetvp(dp, bp); bp->b_bcount = bsize; bp->b_bufsize = bsize; /* do the input */ VOP_STRATEGY(bp); /* we definitely need to be at splbio here */ s = splbio(); while ((bp->b_flags & B_DONE) == 0) { tsleep(bp, PVM, "vnsrd", 0); } splx(s); if ((bp->b_flags & B_ERROR) != 0) error = EIO; /* * free the buffer header back to the swap buffer pool */ relpbuf(bp); if (error) break; vm_page_set_validclean(m, (i * bsize) & PAGE_MASK, bsize); } else { vm_page_set_validclean(m, (i * bsize) & PAGE_MASK, bsize); bzero((caddr_t) kva + i * bsize, bsize); } } vm_pager_unmap_page(kva); pmap_clear_modify(VM_PAGE_TO_PHYS(m)); m->flags &= ~PG_ZERO; if (error) { return VM_PAGER_ERROR; } return VM_PAGER_OK; } /* * old style vnode pager output routine */ static int vnode_pager_input_old(object, m) vm_object_t object; vm_page_t m; { struct uio auio; struct iovec aiov; int error; int size; vm_offset_t kva; error = 0; /* * Return failure if beyond current EOF */ if (IDX_TO_OFF(m->pindex) >= object->un_pager.vnp.vnp_size) { return VM_PAGER_BAD; } else { size = PAGE_SIZE; if (IDX_TO_OFF(m->pindex) + size > object->un_pager.vnp.vnp_size) size = object->un_pager.vnp.vnp_size - IDX_TO_OFF(m->pindex); /* * Allocate a kernel virtual address and initialize so that * we can use VOP_READ/WRITE routines. */ kva = vm_pager_map_page(m); aiov.iov_base = (caddr_t) kva; aiov.iov_len = size; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = IDX_TO_OFF(m->pindex); auio.uio_segflg = UIO_SYSSPACE; auio.uio_rw = UIO_READ; auio.uio_resid = size; auio.uio_procp = (struct proc *) 0; error = VOP_READ(object->handle, &auio, 0, curproc->p_ucred); if (!error) { register int count = size - auio.uio_resid; if (count == 0) error = EINVAL; else if (count != PAGE_SIZE) bzero((caddr_t) kva + count, PAGE_SIZE - count); } vm_pager_unmap_page(kva); } pmap_clear_modify(VM_PAGE_TO_PHYS(m)); m->dirty = 0; m->flags &= ~PG_ZERO; return error ? VM_PAGER_ERROR : VM_PAGER_OK; } /* * generic vnode pager input routine */ static int vnode_pager_getpages(object, m, count, reqpage) vm_object_t object; vm_page_t *m; int count; int reqpage; { int rtval; struct vnode *vp; + if (object->flags & OBJ_VNODE_GONE) + return VM_PAGER_ERROR; vp = object->handle; rtval = VOP_GETPAGES(vp, m, count*PAGE_SIZE, reqpage, 0); if (rtval == EOPNOTSUPP) return vnode_pager_leaf_getpages(object, m, count, reqpage); else return rtval; } static int vnode_pager_leaf_getpages(object, m, count, reqpage) vm_object_t object; vm_page_t *m; int count; int reqpage; { vm_offset_t kva; off_t foff; int i, size, bsize, first, firstaddr; struct vnode *dp, *vp; int runpg; int runend; struct buf *bp; int s; int error = 0; vp = object->handle; if (vp->v_mount == NULL) return VM_PAGER_BAD; bsize = vp->v_mount->mnt_stat.f_iosize; /* get the UNDERLYING device for the file with VOP_BMAP() */ /* * originally, we did not check for an error return value -- assuming * an fs always has a bmap entry point -- that assumption is wrong!!! */ foff = IDX_TO_OFF(m[reqpage]->pindex); /* * if we can't bmap, use old VOP code */ if (VOP_BMAP(vp, 0, &dp, 0, NULL, NULL)) { for (i = 0; i < count; i++) { if (i != reqpage) { vnode_pager_freepage(m[i]); } } cnt.v_vnodein++; cnt.v_vnodepgsin++; return vnode_pager_input_old(object, m[reqpage]); /* * if the blocksize is smaller than a page size, then use * special small filesystem code. NFS sometimes has a small * blocksize, but it can handle large reads itself. */ } else if ((PAGE_SIZE / bsize) > 1 && (vp->v_mount->mnt_stat.f_type != MOUNT_NFS)) { for (i = 0; i < count; i++) { if (i != reqpage) { vnode_pager_freepage(m[i]); } } cnt.v_vnodein++; cnt.v_vnodepgsin++; return vnode_pager_input_smlfs(object, m[reqpage]); } /* * if ANY DEV_BSIZE blocks are valid on a large filesystem block * then, the entire page is valid -- */ if (m[reqpage]->valid) { m[reqpage]->valid = VM_PAGE_BITS_ALL; for (i = 0; i < count; i++) { if (i != reqpage) vnode_pager_freepage(m[i]); } return VM_PAGER_OK; } /* * here on direct device I/O */ firstaddr = -1; /* * calculate the run that includes the required page */ for(first = 0, i = 0; i < count; i = runend) { firstaddr = vnode_pager_addr(vp, IDX_TO_OFF(m[i]->pindex), &runpg); if (firstaddr == -1) { if (i == reqpage && foff < object->un_pager.vnp.vnp_size) { panic("vnode_pager_putpages: unexpected missing page: firstaddr: %d, foff: %ld, vnp_size: %d", firstaddr, foff, object->un_pager.vnp.vnp_size); } vnode_pager_freepage(m[i]); runend = i + 1; first = runend; continue; } runend = i + runpg; if (runend <= reqpage) { int j; for (j = i; j < runend; j++) { vnode_pager_freepage(m[j]); } } else { if (runpg < (count - first)) { for (i = first + runpg; i < count; i++) vnode_pager_freepage(m[i]); count = first + runpg; } break; } first = runend; } /* * the first and last page have been calculated now, move input pages * to be zero based... */ if (first != 0) { for (i = first; i < count; i++) { m[i - first] = m[i]; } count -= first; reqpage -= first; } /* * calculate the file virtual address for the transfer */ foff = IDX_TO_OFF(m[0]->pindex); /* * calculate the size of the transfer */ size = count * PAGE_SIZE; if ((foff + size) > object->un_pager.vnp.vnp_size) size = object->un_pager.vnp.vnp_size - foff; /* * round up physical size for real devices */ if (dp->v_type == VBLK || dp->v_type == VCHR) size = (size + DEV_BSIZE - 1) & ~(DEV_BSIZE - 1); bp = getpbuf(); kva = (vm_offset_t) bp->b_data; /* * and map the pages to be read into the kva */ pmap_qenter(kva, m, count); /* build a minimal buffer header */ bp->b_flags = B_BUSY | B_READ | B_CALL; bp->b_iodone = vnode_pager_iodone; /* B_PHYS is not set, but it is nice to fill this in */ bp->b_proc = curproc; bp->b_rcred = bp->b_wcred = bp->b_proc->p_ucred; if (bp->b_rcred != NOCRED) crhold(bp->b_rcred); if (bp->b_wcred != NOCRED) crhold(bp->b_wcred); bp->b_blkno = firstaddr; pbgetvp(dp, bp); bp->b_bcount = size; bp->b_bufsize = size; cnt.v_vnodein++; cnt.v_vnodepgsin += count; /* do the input */ VOP_STRATEGY(bp); s = splbio(); /* we definitely need to be at splbio here */ while ((bp->b_flags & B_DONE) == 0) { tsleep(bp, PVM, "vnread", 0); } splx(s); if ((bp->b_flags & B_ERROR) != 0) error = EIO; if (!error) { if (size != count * PAGE_SIZE) bzero((caddr_t) kva + size, PAGE_SIZE * count - size); } pmap_qremove(kva, count); /* * free the buffer header back to the swap buffer pool */ relpbuf(bp); for (i = 0; i < count; i++) { pmap_clear_modify(VM_PAGE_TO_PHYS(m[i])); m[i]->dirty = 0; m[i]->valid = VM_PAGE_BITS_ALL; m[i]->flags &= ~PG_ZERO; if (i != reqpage) { /* * whether or not to leave the page activated is up in * the air, but we should put the page on a page queue * somewhere. (it already is in the object). Result: * It appears that emperical results show that * deactivating pages is best. */ /* * just in case someone was asking for this page we * now tell them that it is ok to use */ if (!error) { vm_page_deactivate(m[i]); PAGE_WAKEUP(m[i]); } else { vnode_pager_freepage(m[i]); } } } if (error) { printf("vnode_pager_getpages: I/O read error\n"); } return (error ? VM_PAGER_ERROR : VM_PAGER_OK); } static int vnode_pager_putpages(object, m, count, sync, rtvals) vm_object_t object; vm_page_t *m; int count; boolean_t sync; int *rtvals; { int rtval; struct vnode *vp; + + if (object->flags & OBJ_VNODE_GONE) + return VM_PAGER_ERROR; + vp = object->handle; rtval = VOP_PUTPAGES(vp, m, count*PAGE_SIZE, sync, rtvals, 0); if (rtval == EOPNOTSUPP) return vnode_pager_leaf_putpages(object, m, count, sync, rtvals); else return rtval; } /* * generic vnode pager output routine */ static int vnode_pager_leaf_putpages(object, m, count, sync, rtvals) vm_object_t object; vm_page_t *m; int count; boolean_t sync; int *rtvals; { int i; struct vnode *vp; int maxsize, ncount; vm_ooffset_t poffset; struct uio auio; struct iovec aiov; int error; vp = object->handle;; for (i = 0; i < count; i++) rtvals[i] = VM_PAGER_AGAIN; if ((int) m[0]->pindex < 0) { printf("vnode_pager_putpages: attempt to write meta-data!!! -- 0x%x(%x)\n", m[0]->pindex, m[0]->dirty); rtvals[0] = VM_PAGER_BAD; return VM_PAGER_BAD; } maxsize = count * PAGE_SIZE; ncount = count; poffset = IDX_TO_OFF(m[0]->pindex); if (maxsize + poffset > object->un_pager.vnp.vnp_size) { if (object->un_pager.vnp.vnp_size > poffset) maxsize = object->un_pager.vnp.vnp_size - poffset; else maxsize = 0; ncount = btoc(maxsize); if (ncount < count) { for (i = ncount; i < count; i++) { rtvals[i] = VM_PAGER_BAD; } #ifdef BOGUS if (ncount == 0) { printf("vnode_pager_putpages: write past end of file: %d, %lu\n", poffset, (unsigned long) object->un_pager.vnp.vnp_size); return rtvals[0]; } #endif } } for (i = 0; i < count; i++) { m[i]->busy++; m[i]->flags &= ~PG_BUSY; } aiov.iov_base = (caddr_t) 0; aiov.iov_len = maxsize; auio.uio_iov = &aiov; auio.uio_iovcnt = 1; auio.uio_offset = poffset; auio.uio_segflg = UIO_NOCOPY; auio.uio_rw = UIO_WRITE; auio.uio_resid = maxsize; auio.uio_procp = (struct proc *) 0; error = VOP_WRITE(vp, &auio, IO_VMIO|(sync?IO_SYNC:0), curproc->p_ucred); cnt.v_vnodeout++; cnt.v_vnodepgsout += ncount; if (error) { printf("vnode_pager_putpages: I/O error %d\n", error); } if (auio.uio_resid) { printf("vnode_pager_putpages: residual I/O %d at %ld\n", auio.uio_resid, m[0]->pindex); } for (i = 0; i < count; i++) { m[i]->busy--; if (i < ncount) { rtvals[i] = VM_PAGER_OK; } if ((m[i]->busy == 0) && (m[i]->flags & PG_WANTED)) wakeup(m[i]); } return rtvals[0]; } struct vnode * vnode_pager_lock(object) vm_object_t object; { for (; object != NULL; object = object->backing_object) { if (object->type != OBJT_VNODE) continue; VOP_LOCK(object->handle); return object->handle; } return NULL; }