pseudofs_vncache.c revision 227309
175295Sdes/*- 275295Sdes * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav 375295Sdes * All rights reserved. 475295Sdes * 575295Sdes * Redistribution and use in source and binary forms, with or without 675295Sdes * modification, are permitted provided that the following conditions 775295Sdes * are met: 875295Sdes * 1. Redistributions of source code must retain the above copyright 975295Sdes * notice, this list of conditions and the following disclaimer 1075295Sdes * in this position and unchanged. 1175295Sdes * 2. Redistributions in binary form must reproduce the above copyright 1275295Sdes * notice, this list of conditions and the following disclaimer in the 1375295Sdes * documentation and/or other materials provided with the distribution. 1475295Sdes * 3. The name of the author may not be used to endorse or promote products 1575295Sdes * derived from this software without specific prior written permission. 1675295Sdes * 1775295Sdes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1875295Sdes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1975295Sdes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2075295Sdes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2175295Sdes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2275295Sdes * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2375295Sdes * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2475295Sdes * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2575295Sdes * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2675295Sdes * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2775295Sdes */ 2875295Sdes 29143592Sdes#include <sys/cdefs.h> 30143592Sdes__FBSDID("$FreeBSD: head/sys/fs/pseudofs/pseudofs_vncache.c 227309 2011-11-07 15:43:11Z ed $"); 31143592Sdes 32143592Sdes#include "opt_pseudofs.h" 33143592Sdes 3475295Sdes#include <sys/param.h> 3575295Sdes#include <sys/kernel.h> 3675295Sdes#include <sys/systm.h> 37112564Sjhb#include <sys/eventhandler.h> 3878073Sdes#include <sys/lock.h> 3975295Sdes#include <sys/malloc.h> 4077965Sdes#include <sys/mutex.h> 4184246Sdes#include <sys/proc.h> 4275295Sdes#include <sys/sysctl.h> 4375295Sdes#include <sys/vnode.h> 4475295Sdes 4575295Sdes#include <fs/pseudofs/pseudofs.h> 4675295Sdes#include <fs/pseudofs/pseudofs_internal.h> 4775295Sdes 4877998Sdesstatic MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 4975295Sdes 5075295Sdesstatic struct mtx pfs_vncache_mutex; 5189071Smsmithstatic struct pfs_vdata *pfs_vncache; 52112564Sjhbstatic eventhandler_tag pfs_exit_tag; 53112564Sjhbstatic void pfs_exit(void *arg, struct proc *p); 5475295Sdes 55227309Sedstatic SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 5675295Sdes "pseudofs vnode cache"); 5775295Sdes 5884246Sdesstatic int pfs_vncache_entries; 5984246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 6084246Sdes &pfs_vncache_entries, 0, 6184246Sdes "number of entries in the vnode cache"); 6284246Sdes 6384246Sdesstatic int pfs_vncache_maxentries; 6484246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 6584246Sdes &pfs_vncache_maxentries, 0, 6684246Sdes "highest number of entries in the vnode cache"); 6784246Sdes 6875295Sdesstatic int pfs_vncache_hits; 6984246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 7084246Sdes &pfs_vncache_hits, 0, 7175295Sdes "number of cache hits since initialization"); 7275295Sdes 7375295Sdesstatic int pfs_vncache_misses; 7484246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 7584246Sdes &pfs_vncache_misses, 0, 7675295Sdes "number of cache misses since initialization"); 7775295Sdes 78138290Sphkextern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ 7975295Sdes 8075295Sdes/* 8175295Sdes * Initialize vnode cache 8275295Sdes */ 8375295Sdesvoid 8475295Sdespfs_vncache_load(void) 8575295Sdes{ 86168720Sdes 87168720Sdes mtx_assert(&Giant, MA_OWNED); 88168720Sdes mtx_init(&pfs_vncache_mutex, "pfs_vncache", NULL, MTX_DEF); 89112564Sjhb pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 90112564Sjhb EVENTHANDLER_PRI_ANY); 9175295Sdes} 9275295Sdes 9375295Sdes/* 9475295Sdes * Tear down vnode cache 9575295Sdes */ 9675295Sdesvoid 9775295Sdespfs_vncache_unload(void) 9875295Sdes{ 99168720Sdes 100168720Sdes mtx_assert(&Giant, MA_OWNED); 101112564Sjhb EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 102168720Sdes KASSERT(pfs_vncache_entries == 0, 103168720Sdes ("%d vncache entries remaining", pfs_vncache_entries)); 10475295Sdes mtx_destroy(&pfs_vncache_mutex); 10575295Sdes} 10675295Sdes 10775295Sdes/* 10875295Sdes * Allocate a vnode 10975295Sdes */ 11075295Sdesint 11177998Sdespfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 11277998Sdes struct pfs_node *pn, pid_t pid) 11375295Sdes{ 114186560Skib struct pfs_vdata *pvd, *pvd2; 115165737Sjhb struct vnode *vp; 11675295Sdes int error; 11788234Sdillon 11888234Sdillon /* 11997940Sdes * See if the vnode is in the cache. 12088234Sdillon * XXX linear search is not very efficient. 12188234Sdillon */ 122165737Sjhbretry: 12375295Sdes mtx_lock(&pfs_vncache_mutex); 12484246Sdes for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 125109969Stjr if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 126109969Stjr pvd->pvd_vnode->v_mount == mp) { 127165737Sjhb vp = pvd->pvd_vnode; 128165737Sjhb VI_LOCK(vp); 129165737Sjhb mtx_unlock(&pfs_vncache_mutex); 130165737Sjhb if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { 13175295Sdes ++pfs_vncache_hits; 132165737Sjhb *vpp = vp; 133168637Sdes /* 134168637Sdes * Some callers cache_enter(vp) later, so 135168637Sdes * we have to make sure it's not in the 136168637Sdes * VFS cache so it doesn't get entered 137168637Sdes * twice. A better solution would be to 138168637Sdes * make pfs_vncache_alloc() responsible 139168637Sdes * for entering the vnode in the VFS 140168637Sdes * cache. 141168637Sdes */ 142168637Sdes cache_purge(vp); 14375295Sdes return (0); 14475295Sdes } 145165737Sjhb goto retry; 14677998Sdes } 14777998Sdes } 14877998Sdes mtx_unlock(&pfs_vncache_mutex); 14975295Sdes 15075295Sdes /* nope, get a new one */ 151184205Sdes pvd = malloc(sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 152186565Skib pvd->pvd_next = pvd->pvd_prev = NULL; 153138290Sphk error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); 154105165Sphk if (error) { 155184205Sdes free(pvd, M_PFSVNCACHE); 15675295Sdes return (error); 157105165Sphk } 15877998Sdes pvd->pvd_pn = pn; 15977998Sdes pvd->pvd_pid = pid; 16077998Sdes (*vpp)->v_data = pvd; 16175295Sdes switch (pn->pn_type) { 16275295Sdes case pfstype_root: 163101308Sjeff (*vpp)->v_vflag = VV_ROOT; 16475295Sdes#if 0 16575295Sdes printf("root vnode allocated\n"); 16675295Sdes#endif 16784246Sdes /* fall through */ 16875295Sdes case pfstype_dir: 16975295Sdes case pfstype_this: 17075295Sdes case pfstype_parent: 17177998Sdes case pfstype_procdir: 17275295Sdes (*vpp)->v_type = VDIR; 17375295Sdes break; 17475295Sdes case pfstype_file: 17575295Sdes (*vpp)->v_type = VREG; 17675295Sdes break; 17775295Sdes case pfstype_symlink: 17875295Sdes (*vpp)->v_type = VLNK; 17975295Sdes break; 18077998Sdes case pfstype_none: 18177998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 18275295Sdes default: 18375295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 18475295Sdes } 185103314Snjl /* 186103314Snjl * Propagate flag through to vnode so users know it can change 187103314Snjl * if the process changes (i.e. execve) 188103314Snjl */ 189103314Snjl if ((pn->pn_flags & PFS_PROCDEP) != 0) 190103314Snjl (*vpp)->v_vflag |= VV_PROCDEP; 19184246Sdes pvd->pvd_vnode = *vpp; 192211531Sjhb vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); 193176519Sattilio VN_LOCK_AREC(*vpp); 194167497Stegge error = insmntque(*vpp, mp); 195167497Stegge if (error != 0) { 196196920Skib free(pvd, M_PFSVNCACHE); 197167497Stegge *vpp = NULLVP; 198167497Stegge return (error); 199167497Stegge } 200186560Skibretry2: 20177998Sdes mtx_lock(&pfs_vncache_mutex); 202186560Skib /* 203186560Skib * Other thread may race with us, creating the entry we are 204186560Skib * going to insert into the cache. Recheck after 205186560Skib * pfs_vncache_mutex is reacquired. 206186560Skib */ 207186560Skib for (pvd2 = pfs_vncache; pvd2; pvd2 = pvd2->pvd_next) { 208186560Skib if (pvd2->pvd_pn == pn && pvd2->pvd_pid == pid && 209186560Skib pvd2->pvd_vnode->v_mount == mp) { 210186560Skib vp = pvd2->pvd_vnode; 211186560Skib VI_LOCK(vp); 212186560Skib mtx_unlock(&pfs_vncache_mutex); 213186560Skib if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { 214186560Skib ++pfs_vncache_hits; 215186560Skib vgone(*vpp); 216186981Smarcus vput(*vpp); 217186560Skib *vpp = vp; 218186560Skib cache_purge(vp); 219186560Skib return (0); 220186560Skib } 221186560Skib goto retry2; 222186560Skib } 223186560Skib } 224186560Skib ++pfs_vncache_misses; 225186560Skib if (++pfs_vncache_entries > pfs_vncache_maxentries) 226186560Skib pfs_vncache_maxentries = pfs_vncache_entries; 22784246Sdes pvd->pvd_prev = NULL; 22884246Sdes pvd->pvd_next = pfs_vncache; 22984246Sdes if (pvd->pvd_next) 23084246Sdes pvd->pvd_next->pvd_prev = pvd; 23184246Sdes pfs_vncache = pvd; 23275295Sdes mtx_unlock(&pfs_vncache_mutex); 23375295Sdes return (0); 23475295Sdes} 23575295Sdes 23675295Sdes/* 23775295Sdes * Free a vnode 23875295Sdes */ 23975295Sdesint 24075295Sdespfs_vncache_free(struct vnode *vp) 24175295Sdes{ 24277998Sdes struct pfs_vdata *pvd; 24388234Sdillon 24475295Sdes mtx_lock(&pfs_vncache_mutex); 24584246Sdes pvd = (struct pfs_vdata *)vp->v_data; 24684246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 24784246Sdes if (pvd->pvd_next) 24884246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 249196921Skib if (pvd->pvd_prev) { 25084246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 251196921Skib --pfs_vncache_entries; 252196921Skib } else if (pfs_vncache == pvd) { 25384246Sdes pfs_vncache = pvd->pvd_next; 254196921Skib --pfs_vncache_entries; 255196921Skib } 25677998Sdes mtx_unlock(&pfs_vncache_mutex); 25784246Sdes 258184205Sdes free(pvd, M_PFSVNCACHE); 25975295Sdes vp->v_data = NULL; 26075295Sdes return (0); 26175295Sdes} 26284246Sdes 26384246Sdes/* 264168764Sdes * Purge the cache of dead entries 265139896Srwatson * 266168637Sdes * This is extremely inefficient due to the fact that vgone() not only 267168637Sdes * indirectly modifies the vnode cache, but may also sleep. We can 268168637Sdes * neither hold pfs_vncache_mutex across a vgone() call, nor make any 269168637Sdes * assumptions about the state of the cache after vgone() returns. In 270168637Sdes * consequence, we must start over after every vgone() call, and keep 271168637Sdes * trying until we manage to traverse the entire cache. 272168637Sdes * 273168637Sdes * The only way to improve this situation is to change the data structure 274168637Sdes * used to implement the cache. 27584246Sdes */ 276193556Sdesstatic void 277193556Sdespfs_purge_locked(struct pfs_node *pn) 27884246Sdes{ 279133776Sdes struct pfs_vdata *pvd; 280133776Sdes struct vnode *vnp; 28184246Sdes 282193556Sdes mtx_assert(&pfs_vncache_mutex, MA_OWNED); 283133776Sdes pvd = pfs_vncache; 284133776Sdes while (pvd != NULL) { 285168637Sdes if (pvd->pvd_dead || (pn != NULL && pvd->pvd_pn == pn)) { 286133776Sdes vnp = pvd->pvd_vnode; 287147809Sjeff vhold(vnp); 288133776Sdes mtx_unlock(&pfs_vncache_mutex); 289175294Sattilio VOP_LOCK(vnp, LK_EXCLUSIVE); 290133776Sdes vgone(vnp); 291175294Sattilio VOP_UNLOCK(vnp, 0); 292193556Sdes mtx_lock(&pfs_vncache_mutex); 293147809Sjeff vdrop(vnp); 294133776Sdes pvd = pfs_vncache; 295133776Sdes } else { 296133776Sdes pvd = pvd->pvd_next; 29784246Sdes } 29884246Sdes } 299193556Sdes} 300193556Sdes 301193556Sdesvoid 302193556Sdespfs_purge(struct pfs_node *pn) 303193556Sdes{ 304193556Sdes 305193556Sdes mtx_lock(&pfs_vncache_mutex); 306193556Sdes pfs_purge_locked(pn); 30784246Sdes mtx_unlock(&pfs_vncache_mutex); 308168637Sdes} 309168637Sdes 310168637Sdes/* 311168637Sdes * Free all vnodes associated with a defunct process 312168637Sdes */ 313168637Sdesstatic void 314168637Sdespfs_exit(void *arg, struct proc *p) 315168637Sdes{ 316168637Sdes struct pfs_vdata *pvd; 317168637Sdes int dead; 318168637Sdes 319168637Sdes if (pfs_vncache == NULL) 320168637Sdes return; 321168637Sdes mtx_lock(&pfs_vncache_mutex); 322168637Sdes for (pvd = pfs_vncache, dead = 0; pvd != NULL; pvd = pvd->pvd_next) 323168637Sdes if (pvd->pvd_pid == p->p_pid) 324168637Sdes dead = pvd->pvd_dead = 1; 325193556Sdes if (dead) 326193556Sdes pfs_purge_locked(NULL); 327168637Sdes mtx_unlock(&pfs_vncache_mutex); 32884246Sdes} 329