pseudofs_vncache.c revision 112564
1133808Spjd/*- 2133808Spjd * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav 3133808Spjd * All rights reserved. 4133808Spjd * 5216468Sobrien * Redistribution and use in source and binary forms, with or without 6133808Spjd * modification, are permitted provided that the following conditions 7133808Spjd * are met: 8133808Spjd * 1. Redistributions of source code must retain the above copyright 9133808Spjd * notice, this list of conditions and the following disclaimer 10133808Spjd * 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: head/sys/fs/pseudofs/pseudofs_vncache.c 112564 2003-03-24 21:15:35Z jhb $ 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 45static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 46 47static struct mtx pfs_vncache_mutex; 48static struct pfs_vdata *pfs_vncache; 49static eventhandler_tag pfs_exit_tag; 50static void pfs_exit(void *arg, struct proc *p); 51 52SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 53 "pseudofs vnode cache"); 54 55static int pfs_vncache_entries; 56SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 57 &pfs_vncache_entries, 0, 58 "number of entries in the vnode cache"); 59 60static int pfs_vncache_maxentries; 61SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 62 &pfs_vncache_maxentries, 0, 63 "highest number of entries in the vnode cache"); 64 65static int pfs_vncache_hits; 66SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 67 &pfs_vncache_hits, 0, 68 "number of cache hits since initialization"); 69 70static int pfs_vncache_misses; 71SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 72 &pfs_vncache_misses, 0, 73 "number of cache misses since initialization"); 74 75extern vop_t **pfs_vnodeop_p; 76 77/* 78 * Initialize vnode cache 79 */ 80void 81pfs_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 */ 92void 93pfs_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 */ 105int 106pfs_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 */ 195int 196pfs_vncache_free(struct vnode *vp) 197{ 198 struct pfs_vdata *pvd; 199 200 cache_purge(vp); 201 202 mtx_lock(&pfs_vncache_mutex); 203 pvd = (struct pfs_vdata *)vp->v_data; 204 KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 205 if (pvd->pvd_next) 206 pvd->pvd_next->pvd_prev = pvd->pvd_prev; 207 if (pvd->pvd_prev) 208 pvd->pvd_prev->pvd_next = pvd->pvd_next; 209 else 210 pfs_vncache = pvd->pvd_next; 211 mtx_unlock(&pfs_vncache_mutex); 212 213 --pfs_vncache_entries; 214 FREE(pvd, M_PFSVNCACHE); 215 vp->v_data = NULL; 216 return (0); 217} 218 219/* 220 * Free all vnodes associated with a defunct process 221 */ 222static void 223pfs_exit(void *arg, struct proc *p) 224{ 225 struct pfs_vdata *pvd, *prev; 226 227 mtx_lock(&pfs_vncache_mutex); 228 /* 229 * The double loop is necessary because vgone() indirectly 230 * calls pfs_vncache_free() which frees pvd, so we have to 231 * backtrace one step every time we free a vnode. 232 */ 233 /* XXX linear search... not very efficient */ 234 for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 235 while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 236 prev = pvd->pvd_prev; 237 vgone(pvd->pvd_vnode); 238 pvd = prev ? prev->pvd_next : pfs_vncache; 239 } 240 if (pvd == NULL) 241 break; 242 } 243 mtx_unlock(&pfs_vncache_mutex); 244} 245 246/* 247 * Disable a pseudofs node, and free all vnodes associated with it 248 */ 249int 250pfs_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 */ 276int 277pfs_enable(struct pfs_node *pn) 278{ 279 pn->pn_flags &= ~PFS_DISABLED; 280 return (0); 281} 282