pseudofs_vncache.c revision 142907
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 142907 2005-03-01 12:20:49Z phk $ 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 struct vop_vector pfs_vnodeops; /* XXX -> .h file */ 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_vnodeops, 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 * 219 * XXXRW: It is unfortunate that pfs_exit() always acquires and releases two 220 * mutexes (one of which is Giant) for every process exit, even if procfs 221 * isn't mounted. 222 */ 223static void 224pfs_exit(void *arg, struct proc *p) 225{ 226 struct pfs_vdata *pvd; 227 struct vnode *vnp; 228 229 if (pfs_vncache == NULL) 230 return; 231 mtx_lock(&Giant); 232 /* 233 * This is extremely inefficient due to the fact that vgone() not 234 * only indirectly modifies the vnode cache, but may also sleep. 235 * We can neither hold pfs_vncache_mutex across a vgone() call, 236 * nor make any assumptions about the state of the cache after 237 * vgone() returns. In consequence, we must start over after 238 * every vgone() call, and keep trying until we manage to traverse 239 * the entire cache. 240 * 241 * The only way to improve this situation is to change the data 242 * structure used to implement the cache. An obvious choice in 243 * this particular case would be a BST sorted by PID. 244 */ 245 mtx_lock(&pfs_vncache_mutex); 246 pvd = pfs_vncache; 247 while (pvd != NULL) { 248 if (pvd->pvd_pid == p->p_pid) { 249 vnp = pvd->pvd_vnode; 250 mtx_unlock(&pfs_vncache_mutex); 251 vgone(vnp); 252 mtx_lock(&pfs_vncache_mutex); 253 pvd = pfs_vncache; 254 } else { 255 pvd = pvd->pvd_next; 256 } 257 } 258 mtx_unlock(&pfs_vncache_mutex); 259 mtx_unlock(&Giant); 260} 261 262/* 263 * Disable a pseudofs node, and free all vnodes associated with it 264 */ 265int 266pfs_disable(struct pfs_node *pn) 267{ 268 struct pfs_vdata *pvd; 269 struct vnode *vnp; 270 271 if (pn->pn_flags & PFS_DISABLED) 272 return (0); 273 pn->pn_flags |= PFS_DISABLED; 274 /* XXX see comment above nearly identical code in pfs_exit() */ 275 mtx_lock(&pfs_vncache_mutex); 276 pvd = pfs_vncache; 277 while (pvd != NULL) { 278 if (pvd->pvd_pn == pn) { 279 vnp = pvd->pvd_vnode; 280 mtx_unlock(&pfs_vncache_mutex); 281 vgone(vnp); 282 mtx_lock(&pfs_vncache_mutex); 283 pvd = pfs_vncache; 284 } else { 285 pvd = pvd->pvd_next; 286 } 287 } 288 mtx_unlock(&pfs_vncache_mutex); 289 return (0); 290} 291 292/* 293 * Re-enable a disabled pseudofs node 294 */ 295int 296pfs_enable(struct pfs_node *pn) 297{ 298 pn->pn_flags &= ~PFS_DISABLED; 299 return (0); 300} 301