xref: /csrg-svn/sys/miscfs/union/union_subr.c (revision 65963)
1 /*
2  * Copyright (c) 1994 Jan-Simon Pendry
3  * Copyright (c) 1994
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * This code is derived from software contributed to Berkeley by
7  * Jan-Simon Pendry.
8  *
9  * %sccs.include.redist.c%
10  *
11  *	@(#)union_subr.c	1.2 (Berkeley) 02/01/94
12  */
13 
14 #include <sys/param.h>
15 #include <sys/systm.h>
16 #include <sys/time.h>
17 #include <sys/kernel.h>
18 #include <sys/vnode.h>
19 #include <sys/namei.h>
20 #include <sys/malloc.h>
21 #include "union.h" /*<miscfs/union/union.h>*/
22 
23 static struct union_node *unhead;
24 static int unvplock;
25 
26 int
27 union_init()
28 {
29 
30 	unhead = 0;
31 	unvplock = 0;
32 }
33 
34 /*
35  * allocate a union_node/vnode pair.  the vnode is
36  * referenced and locked.
37  *
38  * all union_nodes are maintained on a singly-linked
39  * list.  new nodes are only allocated when they cannot
40  * be found on this list.  entries on the list are
41  * removed when the vfs reclaim entry is called.
42  *
43  * a single lock is kept for the entire list.  this is
44  * needed because the getnewvnode() function can block
45  * waiting for a vnode to become free, in which case there
46  * may be more than one process trying to get the same
47  * vnode.  this lock is only taken if we are going to
48  * call getnewvnode, since the kernel itself is single-threaded.
49  *
50  * if an entry is found on the list, then call vget() to
51  * take a reference.  this is done because there may be
52  * zero references to it and so it needs to removed from
53  * the vnode free list.
54  */
55 int
56 union_allocvp(vpp, mp, dvp, cnp, uppervp, lowervp)
57 	struct vnode **vpp;
58 	struct mount *mp;
59 	struct vnode *dvp;		/* may be null */
60 	struct componentname *cnp;	/* may be null */
61 	struct vnode *uppervp;		/* may be null */
62 	struct vnode *lowervp;		/* may be null */
63 {
64 	int error;
65 	struct union_node *un;
66 	struct union_node **pp;
67 
68 loop:
69 	for (un = unhead; un != 0; un = un->un_next) {
70 		if ((un->un_lowervp == lowervp ||
71 		     un->un_lowervp == 0) &&
72 		    (un->un_uppervp == uppervp ||
73 		     un->un_uppervp == 0) &&
74 		    (UNIONTOV(un)->v_mount == mp)) {
75 			if (vget(un->un_vnode, 1))
76 				goto loop;
77 			un->un_lowervp = lowervp;
78 			un->un_uppervp = uppervp;
79 			*vpp = un->un_vnode;
80 			return (0);
81 		}
82 	}
83 
84 	/*
85 	 * otherwise lock the vp list while we call getnewvnode
86 	 * since that can block.
87 	 */
88 	if (unvplock & UN_LOCKED) {
89 		unvplock |= UN_WANT;
90 		sleep((caddr_t) &unvplock, PINOD);
91 		goto loop;
92 	}
93 	unvplock |= UN_LOCKED;
94 
95 	error = getnewvnode(VT_UNION, mp, union_vnodeop_p, vpp);
96 	if (error)
97 		goto out;
98 
99 	MALLOC((*vpp)->v_data, void *, sizeof(struct union_node),
100 		M_TEMP, M_WAITOK);
101 
102 	un = VTOUNION(*vpp);
103 	un->un_next = 0;
104 	un->un_dirvp = dvp;
105 	un->un_uppervp = uppervp;
106 	un->un_lowervp = lowervp;
107 	un->un_vnode = *vpp;
108 	un->un_flags = 0;
109 	if (cnp) {
110 		un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
111 		bcopy(cnp->cn_nameptr, un->un_path, cnp->cn_namelen);
112 		un->un_path[cnp->cn_namelen] = '\0';
113 	} else {
114 		un->un_path = 0;
115 	}
116 
117 #ifdef DIAGNOSTIC
118 	un->un_pid = 0;
119 #endif
120 
121 	/* add to union vnode list */
122 	for (pp = &unhead; *pp; pp = &(*pp)->un_next)
123 		continue;
124 	*pp = un;
125 
126 out:
127 	unvplock &= ~UN_LOCKED;
128 
129 	if (unvplock & UN_WANT) {
130 		unvplock &= ~UN_WANT;
131 		wakeup((caddr_t) &unvplock);
132 	}
133 
134 	if (un)
135 		VOP_LOCK(UNIONTOV(un));
136 
137 	return (error);
138 }
139 
140 int
141 union_freevp(vp)
142 	struct vnode *vp;
143 {
144 	struct union_node **unpp;
145 	struct union_node *un = VTOUNION(vp);
146 
147 	for (unpp = &unhead; *unpp != 0; unpp = &(*unpp)->un_next) {
148 		if (*unpp == un) {
149 			*unpp = un->un_next;
150 			break;
151 		}
152 	}
153 
154 	if (un->un_path)
155 		FREE(un->un_path, M_TEMP);
156 
157 	FREE(vp->v_data, M_TEMP);
158 	vp->v_data = 0;
159 	return (0);
160 }
161