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 + -
显示快捷键?