vfs_subr.c
来自「基于组件方式开发操作系统的OSKIT源代码」· C语言 代码 · 共 2,733 行 · 第 1/5 页
C
2,733 行
/* $NetBSD: vfs_subr.c,v 1.142 2000/11/27 08:39:44 chs Exp $ *//*- * Copyright (c) 1997, 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility, * NASA Ames Research Center. * * 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 NetBSD * Foundation, Inc. and its contributors. * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. *//* * 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 *//* * External virtual filesystem routines */#include "opt_ddb.h"#include "opt_compat_netbsd.h"#include "opt_compat_43.h"#include <sys/param.h>#include <sys/systm.h>#include <sys/proc.h>#include <sys/kernel.h>#include <sys/mount.h>#include <sys/time.h>#include <sys/fcntl.h>#include <sys/vnode.h>#include <sys/stat.h>#include <sys/namei.h>#include <sys/ucred.h>#include <sys/buf.h>#include <sys/errno.h>#include <sys/malloc.h>#include <sys/domain.h>#include <sys/mbuf.h>#include <sys/syscallargs.h>#include <sys/device.h>#include <sys/dirent.h>#include <miscfs/specfs/specdev.h>#ifndef OSKIT#include <miscfs/genfs/genfs.h>#include <miscfs/syncfs/syncfs.h>#endif#include <uvm/uvm.h>#include <uvm/uvm_ddb.h>#include <sys/sysctl.h>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,};int doforce = 1; /* 1 => permit forcible unmounting */int prtactive = 0; /* 1 => print out reclaim of active vnodes */extern int dovfsusermount; /* 1 => permit any user to mount filesystems *//* * 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 (in vnode.h) */struct freelst vnode_free_list = TAILQ_HEAD_INITIALIZER(vnode_free_list);struct freelst vnode_hold_list = TAILQ_HEAD_INITIALIZER(vnode_hold_list);struct mntlist mountlist = /* mounted filesystem list */ CIRCLEQ_HEAD_INITIALIZER(mountlist);struct vfs_list_head vfs_list = /* vfs list */ LIST_HEAD_INITIALIZER(vfs_list);struct nfs_public nfs_pub; /* publicly exported FS */#ifndef OSKITstruct simplelock mountlist_slock = SIMPLELOCK_INITIALIZER;static struct simplelock mntid_slock = SIMPLELOCK_INITIALIZER;struct simplelock mntvnode_slock = SIMPLELOCK_INITIALIZER;#endifstruct simplelock vnode_free_list_slock = SIMPLELOCK_INITIALIZER;#ifndef OSKITstruct simplelock spechash_slock = SIMPLELOCK_INITIALIZER;#endif/* * These define the root filesystem and device. */struct mount *rootfs;struct vnode *rootvnode;struct device *root_device; /* root device */struct pool vnode_pool; /* memory pool for vnodes *//* * Local declarations. */void insmntque __P((struct vnode *, struct mount *));int getdevvp __P((dev_t, struct vnode **, enum vtype));void vgoneall __P((struct vnode *));#ifndef OSKITstatic int vfs_hang_addrlist __P((struct mount *, struct netexport *, struct export_args *));static int vfs_free_netcred __P((struct radix_node *, void *));static void vfs_free_addrlist __P((struct netexport *));#endif#ifdef DEBUGvoid printlockedvnodes __P((void));#endif#ifndef OSKIT/* * Initialize the vnode management data structures. */voidvntblinit(){ pool_init(&vnode_pool, sizeof(struct vnode), 0, 0, 0, "vnodepl", 0, pool_page_alloc_nointr, pool_page_free_nointr, M_VNODE); /* * Initialize the filesystem syncer. */ vn_initialize_syncerd();}/* * Mark a mount point as busy. Used to synchronize access and to delay * unmounting. Interlock is not released on failure. */intvfs_busy(mp, flags, interlkp) struct mount *mp; int flags; struct simplelock *interlkp;{ int lkflags; while (mp->mnt_flag & MNT_UNMOUNT) { int gone; if (flags & LK_NOWAIT) return (ENOENT); if ((flags & LK_RECURSEFAIL) && mp->mnt_unmounter != NULL && mp->mnt_unmounter == curproc) return (EDEADLK); if (interlkp) simple_unlock(interlkp); /* * Since all busy locks are shared except the exclusive * lock granted when unmounting, the only place that a * wakeup needs to be done is at the release of the * exclusive lock at the end of dounmount. * * XXX MP: add spinlock protecting mnt_wcnt here once you * can atomically unlock-and-sleep. */ mp->mnt_wcnt++; tsleep((caddr_t)mp, PVFS, "vfs_busy", 0); mp->mnt_wcnt--; gone = mp->mnt_flag & MNT_GONE; if (mp->mnt_wcnt == 0) wakeup(&mp->mnt_wcnt); if (interlkp) simple_lock(interlkp); if (gone) return (ENOENT); } lkflags = LK_SHARED; if (interlkp) lkflags |= LK_INTERLOCK; if (lockmgr(&mp->mnt_lock, lkflags, interlkp)) panic("vfs_busy: unexpected lock failure"); return (0);}/* * Free a busy filesystem. */voidvfs_unbusy(mp) struct mount *mp;{ lockmgr(&mp->mnt_lock, LK_RELEASE, NULL);}/* * Lookup a filesystem type, and if found allocate and initialize * a mount structure for it. * * Devname is usually updated by mount(8) after booting. */intvfs_rootmountalloc(fstypename, devname, mpp) char *fstypename; char *devname; struct mount **mpp;{ struct vfsops *vfsp = NULL; struct mount *mp; for (vfsp = LIST_FIRST(&vfs_list); vfsp != NULL; vfsp = LIST_NEXT(vfsp, vfs_list)) if (!strncmp(vfsp->vfs_name, fstypename, MFSNAMELEN)) break; if (vfsp == NULL) return (ENODEV); mp = malloc((u_long)sizeof(struct mount), M_MOUNT, M_WAITOK); memset((char *)mp, 0, (u_long)sizeof(struct mount)); lockinit(&mp->mnt_lock, PVFS, "vfslock", 0, 0); (void)vfs_busy(mp, LK_NOWAIT, 0); LIST_INIT(&mp->mnt_vnodelist); mp->mnt_op = vfsp; mp->mnt_flag = MNT_RDONLY; mp->mnt_vnodecovered = NULLVP; vfsp->vfs_refcount++; strncpy(mp->mnt_stat.f_fstypename, vfsp->vfs_name, MFSNAMELEN); mp->mnt_stat.f_mntonname[0] = '/'; (void) copystr(devname, mp->mnt_stat.f_mntfromname, MNAMELEN - 1, 0); *mpp = mp; return (0);}/* * Lookup a mount point by filesystem identifier. */struct mount *vfs_getvfs(fsid) fsid_t *fsid;{ struct mount *mp; simple_lock(&mountlist_slock); 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]) { simple_unlock(&mountlist_slock); return (mp); } } simple_unlock(&mountlist_slock); return ((struct mount *)0);}/* * Get a new unique fsid */voidvfs_getnewfsid(mp) struct mount *mp;{ static u_short xxxfs_mntid; fsid_t tfsid; int mtype; simple_lock(&mntid_slock); mtype = makefstype(mp->mnt_op->vfs_name); 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) & 0xff, xxxfs_mntid); tfsid.val[1] = mtype; if (mountlist.cqh_first != (void *)&mountlist) { while (vfs_getvfs(&tfsid)) { tfsid.val[0]++; xxxfs_mntid++; } } mp->mnt_stat.f_fsid.val[0] = tfsid.val[0]; simple_unlock(&mntid_slock);}/* * Make a 'unique' number from a mount type name. */longmakefstype(type) const char *type;{ long rv; for (rv = 0; *type; type++) { rv <<= 2; rv ^= *type; } return rv;}/* * Set vnode attributes to VNOVAL */voidvattr_null(vap) struct vattr *vap;{ vap->va_type = VNON; /* * Assign individually so that it is safe even if size and * sign of each member are varied. */ vap->va_mode = VNOVAL; vap->va_nlink = VNOVAL; vap->va_uid = VNOVAL; vap->va_gid = VNOVAL; vap->va_fsid = VNOVAL; vap->va_fileid = VNOVAL; vap->va_size = VNOVAL; vap->va_blocksize = VNOVAL; vap->va_atime.tv_sec = vap->va_mtime.tv_sec = vap->va_ctime.tv_sec = VNOVAL; vap->va_atime.tv_nsec = vap->va_mtime.tv_nsec = vap->va_ctime.tv_nsec = VNOVAL; vap->va_gen = VNOVAL; vap->va_flags = VNOVAL; vap->va_rdev = VNOVAL; vap->va_bytes = VNOVAL; vap->va_vaflags = 0;}/* * Routines having to do with the management of the vnode table. */extern int (**dead_vnodeop_p) __P((void *));long numvnodes;/* * Return the next vnode from the free list. */intgetnewvnode(tag, mp, vops, vpp) enum vtagtype tag; struct mount *mp; int (**vops) __P((void *)); struct vnode **vpp;{ extern struct uvm_pagerops uvm_vnodeops; struct uvm_object *uobj; struct proc *p = curproc; /* XXX */ struct freelst *listhd; static int toggle; struct vnode *vp; int error = 0;#ifdef DIAGNOSTIC int s;#endif if (mp) { /* * Mark filesystem busy while we're creating a vnode. * If unmount is in progress, this will wait; if the * unmount succeeds (only if umount -f), this will * return an error. If the unmount fails, we'll keep * going afterwards. * (This puts the per-mount vnode list logically under * the protection of the vfs_busy lock). */ error = vfs_busy(mp, LK_RECURSEFAIL, 0); if (error && error != EDEADLK) return error; } /* * We must choose whether to allocate a new vnode or recycle an * existing one. The criterion for allocating a new one is that * the total number of vnodes is less than the number desired or * there are no vnodes on either free list. Generally we only * want to recycle vnodes that have no buffers associated with * them, so we look first on the vnode_free_list. If it is empty, * we next consider vnodes with referencing buffers on the * vnode_hold_list. The toggle ensures that half the time we * will use a buffer from the vnode_hold_list, and half the time * we will allocate a new one unless the list has grown to twice * the desired size. We are reticent to recycle vnodes from the * vnode_hold_list because we will lose the identity of all its * referencing buffers. */ toggle ^= 1; if (numvnodes > 2 * desiredvnodes) toggle = 0; simple_lock(&vnode_free_list_slock); if (numvnodes < desiredvnodes || (TAILQ_FIRST(listhd = &vnode_free_list) == NULL && (TAILQ_FIRST(listhd = &vnode_hold_list) == NULL || toggle))) { simple_unlock(&vnode_free_list_slock); vp = pool_get(&vnode_pool, PR_WAITOK); memset(vp, 0, sizeof(*vp)); simple_lock_init(&vp->v_interlock); numvnodes++; } else { for (vp = TAILQ_FIRST(listhd); vp != NULLVP; vp = TAILQ_NEXT(vp, v_freelist)) { if (simple_lock_try(&vp->v_interlock)) { if ((vp->v_flag & VLAYER) == 0) { break; } if (VOP_ISLOCKED(vp) == 0) break; else simple_unlock(&vp->v_interlock); } } /* * Unless this is a bad time of the month, at most * the first NCPUS items on the free list are * locked, so this is close enough to being empty. */ if (vp == NULLVP) { simple_unlock(&vnode_free_list_slock); if (mp && error != EDEADLK) vfs_unbusy(mp); tablefull("vnode", "increase kern.maxvnodes or NVNODE"); *vpp = 0; return (ENFILE); } if (vp->v_usecount) panic("free vnode isn't, vp %p", vp); TAILQ_REMOVE(listhd, vp, v_freelist); /* see comment on why 0xdeadb is set at end of vgone (below) */ vp->v_freelist.tqe_prev = (struct vnode **)0xdeadb; simple_unlock(&vnode_free_list_slock); vp->v_lease = NULL; if (vp->v_type != VBAD) vgonel(vp, p); else simple_unlock(&vp->v_interlock);#ifdef DIAGNOSTIC if (vp->v_data) panic("cleaned vnode isn't, vp %p", vp); s = splbio(); if (vp->v_numoutput) panic("clean vnode has pending I/O's, vp %p", vp); 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_type = VNON; vp->v_vnlock = &vp->v_lock; lockinit(vp->v_vnlock, PVFS, "vnlock", 0, 0); lockinit(&vp->v_glock, PVFS, "glock", 0, 0); cache_purge(vp); vp->v_tag = tag; vp->v_op = vops; insmntque(vp, mp); *vpp = vp; vp->v_usecount = 1; vp->v_data = 0; simple_lock_init(&vp->v_uvm.u_obj.vmobjlock); /*
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?