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