pseudofs_vncache.c revision 120775
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: head/sys/fs/pseudofs/pseudofs_vncache.c 120775 2003-10-05 02:43:30Z jeff $ 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 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 */ 220static void 221pfs_exit(void *arg, struct proc *p) 222{ 223 struct pfs_vdata *pvd, *prev; 224 225 mtx_lock(&pfs_vncache_mutex); 226 /* 227 * The double loop is necessary because vgone() indirectly 228 * calls pfs_vncache_free() which frees pvd, so we have to 229 * backtrace one step every time we free a vnode. 230 */ 231 /* XXX linear search... not very efficient */ 232 for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 233 while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 234 prev = pvd->pvd_prev; 235 vgone(pvd->pvd_vnode); 236 pvd = prev ? prev->pvd_next : pfs_vncache; 237 } 238 if (pvd == NULL) 239 break; 240 } 241 mtx_unlock(&pfs_vncache_mutex); 242} 243 244/* 245 * Disable a pseudofs node, and free all vnodes associated with it 246 */ 247int 248pfs_disable(struct pfs_node *pn) 249{ 250 struct pfs_vdata *pvd, *prev; 251 252 if (pn->pn_flags & PFS_DISABLED) 253 return (0); 254 mtx_lock(&pfs_vncache_mutex); 255 pn->pn_flags |= PFS_DISABLED; 256 /* see the comment about the double loop in pfs_exit() */ 257 /* XXX linear search... not very efficient */ 258 for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 259 while (pvd != NULL && pvd->pvd_pn == pn) { 260 prev = pvd->pvd_prev; 261 vgone(pvd->pvd_vnode); 262 pvd = prev ? prev->pvd_next : pfs_vncache; 263 } 264 if (pvd == NULL) 265 break; 266 } 267 mtx_unlock(&pfs_vncache_mutex); 268 return (0); 269} 270 271/* 272 * Re-enable a disabled pseudofs node 273 */ 274int 275pfs_enable(struct pfs_node *pn) 276{ 277 pn->pn_flags &= ~PFS_DISABLED; 278 return (0); 279} 280