1 /*- 2 * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/systm.h> 34 #include <sys/eventhandler.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/mutex.h> 38 #include <sys/proc.h> 39 #include <sys/sysctl.h> 40 #include <sys/vnode.h> 41 42 #include <fs/pseudofs/pseudofs.h> 43 #include <fs/pseudofs/pseudofs_internal.h> 44 45 static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 46 47 static struct mtx pfs_vncache_mutex; 48 static struct pfs_vdata *pfs_vncache; 49 static eventhandler_tag pfs_exit_tag; 50 static void pfs_exit(void *arg, struct proc *p); 51 52 SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 53 "pseudofs vnode cache"); 54 55 static int pfs_vncache_entries; 56 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 57 &pfs_vncache_entries, 0, 58 "number of entries in the vnode cache"); 59 60 static int pfs_vncache_maxentries; 61 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 62 &pfs_vncache_maxentries, 0, 63 "highest number of entries in the vnode cache"); 64 65 static int pfs_vncache_hits; 66 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 67 &pfs_vncache_hits, 0, 68 "number of cache hits since initialization"); 69 70 static int pfs_vncache_misses; 71 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 72 &pfs_vncache_misses, 0, 73 "number of cache misses since initialization"); 74 75 extern vop_t **pfs_vnodeop_p; 76 77 /* 78 * Initialize vnode cache 79 */ 80 void 81 pfs_vncache_load(void) 82 { 83 mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, 84 MTX_DEF | MTX_RECURSE); 85 pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 86 EVENTHANDLER_PRI_ANY); 87 } 88 89 /* 90 * Tear down vnode cache 91 */ 92 void 93 pfs_vncache_unload(void) 94 { 95 EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 96 if (pfs_vncache_entries != 0) 97 printf("pfs_vncache_unload(): %d entries remaining\n", 98 pfs_vncache_entries); 99 mtx_destroy(&pfs_vncache_mutex); 100 } 101 102 /* 103 * Allocate a vnode 104 */ 105 int 106 pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 107 struct pfs_node *pn, pid_t pid) 108 { 109 struct pfs_vdata *pvd; 110 int error; 111 112 /* 113 * See if the vnode is in the cache. 114 * XXX linear search is not very efficient. 115 */ 116 mtx_lock(&pfs_vncache_mutex); 117 for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 118 if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 119 pvd->pvd_vnode->v_mount == mp) { 120 if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 121 ++pfs_vncache_hits; 122 *vpp = pvd->pvd_vnode; 123 mtx_unlock(&pfs_vncache_mutex); 124 /* XXX see comment at top of pfs_lookup() */ 125 cache_purge(*vpp); 126 vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 127 curthread); 128 return (0); 129 } 130 /* XXX if this can happen, we're in trouble */ 131 break; 132 } 133 } 134 mtx_unlock(&pfs_vncache_mutex); 135 ++pfs_vncache_misses; 136 137 /* nope, get a new one */ 138 MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 139 if (++pfs_vncache_entries > pfs_vncache_maxentries) 140 pfs_vncache_maxentries = pfs_vncache_entries; 141 error = getnewvnode("pseudofs", mp, pfs_vnodeop_p, vpp); 142 if (error) { 143 FREE(pvd, M_PFSVNCACHE); 144 return (error); 145 } 146 pvd->pvd_pn = pn; 147 pvd->pvd_pid = pid; 148 (*vpp)->v_data = pvd; 149 switch (pn->pn_type) { 150 case pfstype_root: 151 (*vpp)->v_vflag = VV_ROOT; 152 #if 0 153 printf("root vnode allocated\n"); 154 #endif 155 /* fall through */ 156 case pfstype_dir: 157 case pfstype_this: 158 case pfstype_parent: 159 case pfstype_procdir: 160 (*vpp)->v_type = VDIR; 161 break; 162 case pfstype_file: 163 (*vpp)->v_type = VREG; 164 break; 165 case pfstype_symlink: 166 (*vpp)->v_type = VLNK; 167 break; 168 case pfstype_none: 169 KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 170 default: 171 panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 172 } 173 /* 174 * Propagate flag through to vnode so users know it can change 175 * if the process changes (i.e. execve) 176 */ 177 if ((pn->pn_flags & PFS_PROCDEP) != 0) 178 (*vpp)->v_vflag |= VV_PROCDEP; 179 pvd->pvd_vnode = *vpp; 180 mtx_lock(&pfs_vncache_mutex); 181 pvd->pvd_prev = NULL; 182 pvd->pvd_next = pfs_vncache; 183 if (pvd->pvd_next) 184 pvd->pvd_next->pvd_prev = pvd; 185 pfs_vncache = pvd; 186 mtx_unlock(&pfs_vncache_mutex); 187 (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; 188 vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 189 return (0); 190 } 191 192 /* 193 * Free a vnode 194 */ 195 int 196 pfs_vncache_free(struct vnode *vp) 197 { 198 struct pfs_vdata *pvd; 199 200 mtx_lock(&pfs_vncache_mutex); 201 pvd = (struct pfs_vdata *)vp->v_data; 202 KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 203 if (pvd->pvd_next) 204 pvd->pvd_next->pvd_prev = pvd->pvd_prev; 205 if (pvd->pvd_prev) 206 pvd->pvd_prev->pvd_next = pvd->pvd_next; 207 else 208 pfs_vncache = pvd->pvd_next; 209 mtx_unlock(&pfs_vncache_mutex); 210 211 --pfs_vncache_entries; 212 FREE(pvd, M_PFSVNCACHE); 213 vp->v_data = NULL; 214 return (0); 215 } 216 217 /* 218 * Free all vnodes associated with a defunct process 219 */ 220 static void 221 pfs_exit(void *arg, struct proc *p) 222 { 223 struct pfs_vdata *pvd, *prev; 224 225 mtx_lock(&Giant); 226 mtx_lock(&pfs_vncache_mutex); 227 /* 228 * The double loop is necessary because vgone() indirectly 229 * calls pfs_vncache_free() which frees pvd, so we have to 230 * backtrace one step every time we free a vnode. 231 */ 232 /* XXX linear search... not very efficient */ 233 for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 234 while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 235 prev = pvd->pvd_prev; 236 vgone(pvd->pvd_vnode); 237 pvd = prev ? prev->pvd_next : pfs_vncache; 238 } 239 if (pvd == NULL) 240 break; 241 } 242 mtx_unlock(&pfs_vncache_mutex); 243 mtx_unlock(&Giant); 244 } 245 246 /* 247 * Disable a pseudofs node, and free all vnodes associated with it 248 */ 249 int 250 pfs_disable(struct pfs_node *pn) 251 { 252 struct pfs_vdata *pvd, *prev; 253 254 if (pn->pn_flags & PFS_DISABLED) 255 return (0); 256 mtx_lock(&pfs_vncache_mutex); 257 pn->pn_flags |= PFS_DISABLED; 258 /* see the comment about the double loop in pfs_exit() */ 259 /* XXX linear search... not very efficient */ 260 for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 261 while (pvd != NULL && pvd->pvd_pn == pn) { 262 prev = pvd->pvd_prev; 263 vgone(pvd->pvd_vnode); 264 pvd = prev ? prev->pvd_next : pfs_vncache; 265 } 266 if (pvd == NULL) 267 break; 268 } 269 mtx_unlock(&pfs_vncache_mutex); 270 return (0); 271 } 272 273 /* 274 * Re-enable a disabled pseudofs node 275 */ 276 int 277 pfs_enable(struct pfs_node *pn) 278 { 279 pn->pn_flags &= ~PFS_DISABLED; 280 return (0); 281 } 282