union_subr.c

来自「早期freebsd实现」· C语言 代码 · 共 745 行 · 第 1/2 页

C
745
字号
/* * Copyright (c) 1994 Jan-Simon Pendry * Copyright (c) 1994 *	The Regents of the University of California.  All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry. * * 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. * *	@(#)union_subr.c	8.4 (Berkeley) 2/17/94 */#include <sys/param.h>#include <sys/systm.h>#include <sys/time.h>#include <sys/kernel.h>#include <sys/vnode.h>#include <sys/namei.h>#include <sys/malloc.h>#include <sys/file.h>#include <sys/filedesc.h>#include <sys/queue.h>#include <miscfs/union/union.h>#ifdef DIAGNOSTIC#include <sys/proc.h>#endif/* must be power of two, otherwise change UNION_HASH() */#define NHASH 32/* unsigned int ... */#define UNION_HASH(u, l) \	(((((unsigned long) (u)) + ((unsigned long) l)) >> 8) & (NHASH-1))static LIST_HEAD(unhead, union_node) unhead[NHASH];static int unvplock[NHASH];intunion_init(){	int i;	for (i = 0; i < NHASH; i++)		LIST_INIT(&unhead[i]);	bzero((caddr_t) unvplock, sizeof(unvplock));}static intunion_list_lock(ix)	int ix;{	if (unvplock[ix] & UN_LOCKED) {		unvplock[ix] |= UN_WANT;		sleep((caddr_t) &unvplock[ix], PINOD);		return (1);	}	unvplock[ix] |= UN_LOCKED;	return (0);}static voidunion_list_unlock(ix)	int ix;{	unvplock[ix] &= ~UN_LOCKED;	if (unvplock[ix] & UN_WANT) {		unvplock[ix] &= ~UN_WANT;		wakeup((caddr_t) &unvplock[ix]);	}}voidunion_updatevp(un, uppervp, lowervp)	struct union_node *un;	struct vnode *uppervp;	struct vnode *lowervp;{	int ohash = UNION_HASH(un->un_uppervp, un->un_lowervp);	int nhash = UNION_HASH(uppervp, lowervp);	if (ohash != nhash) {		/*		 * Ensure locking is ordered from lower to higher		 * to avoid deadlocks.		 */		if (nhash < ohash) {			int t = ohash;			ohash = nhash;			nhash = t;		}		while (union_list_lock(ohash))			continue;		while (union_list_lock(nhash))			continue;		LIST_REMOVE(un, un_cache);		union_list_unlock(ohash);	} else {			while (union_list_lock(nhash))			continue;	}	if (un->un_lowervp != lowervp) {		if (un->un_lowervp) {			vrele(un->un_lowervp);			if (un->un_path) {				free(un->un_path, M_TEMP);				un->un_path = 0;			}			if (un->un_dirvp) {				vrele(un->un_dirvp);				un->un_dirvp = NULLVP;			}		}		un->un_lowervp = lowervp;	}	if (un->un_uppervp != uppervp) {		if (un->un_uppervp)			vrele(un->un_uppervp);		un->un_uppervp = uppervp;	}	if (ohash != nhash)		LIST_INSERT_HEAD(&unhead[nhash], un, un_cache);	union_list_unlock(nhash);}voidunion_newlower(un, lowervp)	struct union_node *un;	struct vnode *lowervp;{	union_updatevp(un, un->un_uppervp, lowervp);}voidunion_newupper(un, uppervp)	struct union_node *un;	struct vnode *uppervp;{	union_updatevp(un, uppervp, un->un_lowervp);}/* * allocate a union_node/vnode pair.  the vnode is * referenced and locked.  the new vnode is returned * via (vpp).  (mp) is the mountpoint of the union filesystem, * (dvp) is the parent directory where the upper layer object * should exist (but doesn't) and (cnp) is the componentname * information which is partially copied to allow the upper * layer object to be created at a later time.  (uppervp) * and (lowervp) reference the upper and lower layer objects * being mapped.  either, but not both, can be nil. * if supplied, (uppervp) is locked. * the reference is either maintained in the new union_node * object which is allocated, or they are vrele'd. * * all union_nodes are maintained on a singly-linked * list.  new nodes are only allocated when they cannot * be found on this list.  entries on the list are * removed when the vfs reclaim entry is called. * * a single lock is kept for the entire list.  this is * needed because the getnewvnode() function can block * waiting for a vnode to become free, in which case there * may be more than one process trying to get the same * vnode.  this lock is only taken if we are going to * call getnewvnode, since the kernel itself is single-threaded. * * if an entry is found on the list, then call vget() to * take a reference.  this is done because there may be * zero references to it and so it needs to removed from * the vnode free list. */intunion_allocvp(vpp, mp, undvp, dvp, cnp, uppervp, lowervp)	struct vnode **vpp;	struct mount *mp;	struct vnode *undvp;	struct vnode *dvp;		/* may be null */	struct componentname *cnp;	/* may be null */	struct vnode *uppervp;		/* may be null */	struct vnode *lowervp;		/* may be null */{	int error;	struct union_node *un;	struct union_node **pp;	struct vnode *xlowervp = NULLVP;	int hash;	int try;	if (uppervp == NULLVP && lowervp == NULLVP)		panic("union: unidentifiable allocation");	if (uppervp && lowervp && (uppervp->v_type != lowervp->v_type)) {		xlowervp = lowervp;		lowervp = NULLVP;	}loop:	for (try = 0; try < 3; try++) {		switch (try) {		case 0:			if (lowervp == NULLVP)				continue;			hash = UNION_HASH(uppervp, lowervp);			break;		case 1:			if (uppervp == NULLVP)				continue;			hash = UNION_HASH(uppervp, NULLVP);			break;		case 2:			if (lowervp == NULLVP)				continue;			hash = UNION_HASH(NULLVP, lowervp);			break;		}		while (union_list_lock(hash))			continue;		for (un = unhead[hash].lh_first; un != 0;					un = un->un_cache.le_next) {			if ((un->un_lowervp == lowervp ||			     un->un_lowervp == NULLVP) &&			    (un->un_uppervp == uppervp ||			     un->un_uppervp == NULLVP) &&			    (UNIONTOV(un)->v_mount == mp)) {				if (vget(UNIONTOV(un), 0)) {					union_list_unlock(hash);					goto loop;				}				break;			}		}		union_list_unlock(hash);		if (un)			break;	}	if (un) {		/*		 * Obtain a lock on the union_node.		 * uppervp is locked, though un->un_uppervp		 * may not be.  this doesn't break the locking		 * hierarchy since in the case that un->un_uppervp		 * is not yet locked it will be vrele'd and replaced		 * with uppervp.		 */		if ((dvp != NULLVP) && (uppervp == dvp)) {			/*			 * Access ``.'', so (un) will already			 * be locked.  Since this process has			 * the lock on (uppervp) no other			 * process can hold the lock on (un).			 */#ifdef DIAGNOSTIC			if ((un->un_flags & UN_LOCKED) == 0)				panic("union: . not locked");			else if (curproc && un->un_pid != curproc->p_pid &&				    un->un_pid > -1 && curproc->p_pid > -1)				panic("union: allocvp not lock owner");#endif		} else {			if (un->un_flags & UN_LOCKED) {				vrele(UNIONTOV(un));				un->un_flags |= UN_WANT;				sleep((caddr_t) &un->un_flags, PINOD);				goto loop;			}			un->un_flags |= UN_LOCKED;#ifdef DIAGNOSTIC			if (curproc)				un->un_pid = curproc->p_pid;			else				un->un_pid = -1;#endif		}		/*		 * At this point, the union_node is locked,		 * un->un_uppervp may not be locked, and uppervp		 * is locked or nil.		 */		/*		 * Save information about the upper layer.		 */		if (uppervp != un->un_uppervp) {			union_newupper(un, uppervp);		} else if (uppervp) {			vrele(uppervp);		}		if (un->un_uppervp) {			un->un_flags |= UN_ULOCK;			un->un_flags &= ~UN_KLOCK;		}		/*		 * Save information about the lower layer.		 * This needs to keep track of pathname		 * and directory information which union_vn_create		 * might need.		 */		if (lowervp != un->un_lowervp) {			union_newlower(un, lowervp);			if (cnp && (lowervp != NULLVP) &&			    (lowervp->v_type == VREG)) {				un->un_hash = cnp->cn_hash;				un->un_path = malloc(cnp->cn_namelen+1,						M_TEMP, M_WAITOK);				bcopy(cnp->cn_nameptr, un->un_path,						cnp->cn_namelen);				un->un_path[cnp->cn_namelen] = '\0';				VREF(dvp);				un->un_dirvp = dvp;			}		} else if (lowervp) {			vrele(lowervp);		}		*vpp = UNIONTOV(un);		return (0);	}	/*

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?