pseudofs_vncache.c revision 133776
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 133776 2004-08-15 21:58:02Z des $ 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, MTX_DEF); 84 pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 85 EVENTHANDLER_PRI_ANY); 86} 87 88/* 89 * Tear down vnode cache 90 */ 91void 92pfs_vncache_unload(void) 93{ 94 EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 95 if (pfs_vncache_entries != 0) 96 printf("pfs_vncache_unload(): %d entries remaining\n", 97 pfs_vncache_entries); 98 mtx_destroy(&pfs_vncache_mutex); 99} 100 101/* 102 * Allocate a vnode 103 */ 104int 105pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 106 struct pfs_node *pn, pid_t pid) 107{ 108 struct pfs_vdata *pvd; 109 int error; 110 111 /* 112 * See if the vnode is in the cache. 113 * XXX linear search is not very efficient. 114 */ 115 mtx_lock(&pfs_vncache_mutex); 116 for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 117 if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 118 pvd->pvd_vnode->v_mount == mp) { 119 if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 120 ++pfs_vncache_hits; 121 *vpp = pvd->pvd_vnode; 122 mtx_unlock(&pfs_vncache_mutex); 123 /* XXX see comment at top of pfs_lookup() */ 124 cache_purge(*vpp); 125 vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 126 curthread); 127 return (0); 128 } 129 /* XXX if this can happen, we're in trouble */ 130 break; 131 } 132 } 133 mtx_unlock(&pfs_vncache_mutex); 134 ++pfs_vncache_misses; 135 136 /* nope, get a new one */ 137 MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 138 if (++pfs_vncache_entries > pfs_vncache_maxentries) 139 pfs_vncache_maxentries = pfs_vncache_entries; 140 error = getnewvnode("pseudofs", mp, pfs_vnodeop_p, vpp); 141 if (error) { 142 FREE(pvd, M_PFSVNCACHE); 143 return (error); 144 } 145 pvd->pvd_pn = pn; 146 pvd->pvd_pid = pid; 147 (*vpp)->v_data = pvd; 148 switch (pn->pn_type) { 149 case pfstype_root: 150 (*vpp)->v_vflag = VV_ROOT; 151#if 0 152 printf("root vnode allocated\n"); 153#endif 154 /* fall through */ 155 case pfstype_dir: 156 case pfstype_this: 157 case pfstype_parent: 158 case pfstype_procdir: 159 (*vpp)->v_type = VDIR; 160 break; 161 case pfstype_file: 162 (*vpp)->v_type = VREG; 163 break; 164 case pfstype_symlink: 165 (*vpp)->v_type = VLNK; 166 break; 167 case pfstype_none: 168 KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 169 default: 170 panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 171 } 172 /* 173 * Propagate flag through to vnode so users know it can change 174 * if the process changes (i.e. execve) 175 */ 176 if ((pn->pn_flags & PFS_PROCDEP) != 0) 177 (*vpp)->v_vflag |= VV_PROCDEP; 178 pvd->pvd_vnode = *vpp; 179 mtx_lock(&pfs_vncache_mutex); 180 pvd->pvd_prev = NULL; 181 pvd->pvd_next = pfs_vncache; 182 if (pvd->pvd_next) 183 pvd->pvd_next->pvd_prev = pvd; 184 pfs_vncache = pvd; 185 mtx_unlock(&pfs_vncache_mutex); 186 (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; 187 vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 188 return (0); 189} 190 191/* 192 * Free a vnode 193 */ 194int 195pfs_vncache_free(struct vnode *vp) 196{ 197 struct pfs_vdata *pvd; 198 199 mtx_lock(&pfs_vncache_mutex); 200 pvd = (struct pfs_vdata *)vp->v_data; 201 KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 202 if (pvd->pvd_next) 203 pvd->pvd_next->pvd_prev = pvd->pvd_prev; 204 if (pvd->pvd_prev) 205 pvd->pvd_prev->pvd_next = pvd->pvd_next; 206 else 207 pfs_vncache = pvd->pvd_next; 208 mtx_unlock(&pfs_vncache_mutex); 209 210 --pfs_vncache_entries; 211 FREE(pvd, M_PFSVNCACHE); 212 vp->v_data = NULL; 213 return (0); 214} 215 216/* 217 * Free all vnodes associated with a defunct process 218 */ 219static void 220pfs_exit(void *arg, struct proc *p) 221{ 222 struct pfs_vdata *pvd; 223 struct vnode *vnp; 224 225 mtx_lock(&Giant); 226 /* 227 * This is extremely inefficient due to the fact that vgone() not 228 * only indirectly modifies the vnode cache, but may also sleep. 229 * We can neither hold pfs_vncache_mutex across a vgone() call, 230 * nor make any assumptions about the state of the cache after 231 * vgone() returns. In consequence, we must start over after 232 * every vgone() call, and keep trying until we manage to traverse 233 * the entire cache. 234 * 235 * The only way to improve this situation is to change the data 236 * structure used to implement the cache. An obvious choice in 237 * this particular case would be a BST sorted by PID. 238 */ 239 mtx_lock(&pfs_vncache_mutex); 240 pvd = pfs_vncache; 241 while (pvd != NULL) { 242 if (pvd->pvd_pid == p->p_pid) { 243 vnp = pvd->pvd_vnode; 244 mtx_unlock(&pfs_vncache_mutex); 245 vgone(vnp); 246 mtx_lock(&pfs_vncache_mutex); 247 pvd = pfs_vncache; 248 } else { 249 pvd = pvd->pvd_next; 250 } 251 } 252 mtx_unlock(&pfs_vncache_mutex); 253 mtx_unlock(&Giant); 254} 255 256/* 257 * Disable a pseudofs node, and free all vnodes associated with it 258 */ 259int 260pfs_disable(struct pfs_node *pn) 261{ 262 struct pfs_vdata *pvd; 263 struct vnode *vnp; 264 265 if (pn->pn_flags & PFS_DISABLED) 266 return (0); 267 pn->pn_flags |= PFS_DISABLED; 268 /* XXX see comment above nearly identical code in pfs_exit() */ 269 mtx_lock(&pfs_vncache_mutex); 270 pvd = pfs_vncache; 271 while (pvd != NULL) { 272 if (pvd->pvd_pn == pn) { 273 vnp = pvd->pvd_vnode; 274 mtx_unlock(&pfs_vncache_mutex); 275 vgone(vnp); 276 mtx_lock(&pfs_vncache_mutex); 277 pvd = pfs_vncache; 278 } else { 279 pvd = pvd->pvd_next; 280 } 281 } 282 mtx_unlock(&pfs_vncache_mutex); 283 return (0); 284} 285 286/* 287 * Re-enable a disabled pseudofs node 288 */ 289int 290pfs_enable(struct pfs_node *pn) 291{ 292 pn->pn_flags &= ~PFS_DISABLED; 293 return (0); 294} 295