pseudofs_vncache.c revision 139896
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 * $FreeBSD: head/sys/fs/pseudofs/pseudofs_vncache.c 139896 2005-01-08 04:56:38Z rwatson $ 2975295Sdes */ 3075295Sdes 3175295Sdes#include <sys/param.h> 3275295Sdes#include <sys/kernel.h> 3375295Sdes#include <sys/systm.h> 34112564Sjhb#include <sys/eventhandler.h> 3578073Sdes#include <sys/lock.h> 3675295Sdes#include <sys/malloc.h> 3777965Sdes#include <sys/mutex.h> 3884246Sdes#include <sys/proc.h> 3975295Sdes#include <sys/sysctl.h> 4075295Sdes#include <sys/vnode.h> 4175295Sdes 4275295Sdes#include <fs/pseudofs/pseudofs.h> 4375295Sdes#include <fs/pseudofs/pseudofs_internal.h> 4475295Sdes 4577998Sdesstatic MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 4675295Sdes 4775295Sdesstatic struct mtx pfs_vncache_mutex; 4889071Smsmithstatic struct pfs_vdata *pfs_vncache; 49112564Sjhbstatic eventhandler_tag pfs_exit_tag; 50112564Sjhbstatic void pfs_exit(void *arg, struct proc *p); 5175295Sdes 5275295SdesSYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 5375295Sdes "pseudofs vnode cache"); 5475295Sdes 5584246Sdesstatic int pfs_vncache_entries; 5684246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 5784246Sdes &pfs_vncache_entries, 0, 5884246Sdes "number of entries in the vnode cache"); 5984246Sdes 6084246Sdesstatic int pfs_vncache_maxentries; 6184246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 6284246Sdes &pfs_vncache_maxentries, 0, 6384246Sdes "highest number of entries in the vnode cache"); 6484246Sdes 6575295Sdesstatic int pfs_vncache_hits; 6684246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 6784246Sdes &pfs_vncache_hits, 0, 6875295Sdes "number of cache hits since initialization"); 6975295Sdes 7075295Sdesstatic int pfs_vncache_misses; 7184246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 7284246Sdes &pfs_vncache_misses, 0, 7375295Sdes "number of cache misses since initialization"); 7475295Sdes 75138290Sphkextern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ 7675295Sdes 7775295Sdes/* 7875295Sdes * Initialize vnode cache 7975295Sdes */ 8075295Sdesvoid 8175295Sdespfs_vncache_load(void) 8275295Sdes{ 83133776Sdes mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, MTX_DEF); 84112564Sjhb pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 85112564Sjhb EVENTHANDLER_PRI_ANY); 8675295Sdes} 8775295Sdes 8875295Sdes/* 8975295Sdes * Tear down vnode cache 9075295Sdes */ 9175295Sdesvoid 9275295Sdespfs_vncache_unload(void) 9375295Sdes{ 94112564Sjhb EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 9584386Sdes if (pfs_vncache_entries != 0) 9684386Sdes printf("pfs_vncache_unload(): %d entries remaining\n", 9784386Sdes pfs_vncache_entries); 9875295Sdes mtx_destroy(&pfs_vncache_mutex); 9975295Sdes} 10075295Sdes 10175295Sdes/* 10275295Sdes * Allocate a vnode 10375295Sdes */ 10475295Sdesint 10577998Sdespfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 10677998Sdes struct pfs_node *pn, pid_t pid) 10775295Sdes{ 10877998Sdes struct pfs_vdata *pvd; 10975295Sdes int error; 11088234Sdillon 11188234Sdillon /* 11297940Sdes * See if the vnode is in the cache. 11388234Sdillon * XXX linear search is not very efficient. 11488234Sdillon */ 11575295Sdes mtx_lock(&pfs_vncache_mutex); 11684246Sdes for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 117109969Stjr if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 118109969Stjr pvd->pvd_vnode->v_mount == mp) { 11984246Sdes if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 12075295Sdes ++pfs_vncache_hits; 12184246Sdes *vpp = pvd->pvd_vnode; 12275295Sdes mtx_unlock(&pfs_vncache_mutex); 12388234Sdillon /* XXX see comment at top of pfs_lookup() */ 12488234Sdillon cache_purge(*vpp); 12599566Sjeff vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 12699566Sjeff curthread); 12775295Sdes return (0); 12875295Sdes } 12977998Sdes /* XXX if this can happen, we're in trouble */ 13077998Sdes break; 13177998Sdes } 13277998Sdes } 13377998Sdes mtx_unlock(&pfs_vncache_mutex); 13475295Sdes ++pfs_vncache_misses; 13575295Sdes 13675295Sdes /* nope, get a new one */ 137111119Simp MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 13884246Sdes if (++pfs_vncache_entries > pfs_vncache_maxentries) 13984246Sdes pfs_vncache_maxentries = pfs_vncache_entries; 140138290Sphk error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); 141105165Sphk if (error) { 142105165Sphk FREE(pvd, M_PFSVNCACHE); 14375295Sdes return (error); 144105165Sphk } 14577998Sdes pvd->pvd_pn = pn; 14677998Sdes pvd->pvd_pid = pid; 14777998Sdes (*vpp)->v_data = pvd; 14875295Sdes switch (pn->pn_type) { 14975295Sdes case pfstype_root: 150101308Sjeff (*vpp)->v_vflag = VV_ROOT; 15175295Sdes#if 0 15275295Sdes printf("root vnode allocated\n"); 15375295Sdes#endif 15484246Sdes /* fall through */ 15575295Sdes case pfstype_dir: 15675295Sdes case pfstype_this: 15775295Sdes case pfstype_parent: 15877998Sdes case pfstype_procdir: 15975295Sdes (*vpp)->v_type = VDIR; 16075295Sdes break; 16175295Sdes case pfstype_file: 16275295Sdes (*vpp)->v_type = VREG; 16375295Sdes break; 16475295Sdes case pfstype_symlink: 16575295Sdes (*vpp)->v_type = VLNK; 16675295Sdes break; 16777998Sdes case pfstype_none: 16877998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 16975295Sdes default: 17075295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 17175295Sdes } 172103314Snjl /* 173103314Snjl * Propagate flag through to vnode so users know it can change 174103314Snjl * if the process changes (i.e. execve) 175103314Snjl */ 176103314Snjl if ((pn->pn_flags & PFS_PROCDEP) != 0) 177103314Snjl (*vpp)->v_vflag |= VV_PROCDEP; 17884246Sdes pvd->pvd_vnode = *vpp; 17977998Sdes mtx_lock(&pfs_vncache_mutex); 18084246Sdes pvd->pvd_prev = NULL; 18184246Sdes pvd->pvd_next = pfs_vncache; 18284246Sdes if (pvd->pvd_next) 18384246Sdes pvd->pvd_next->pvd_prev = pvd; 18484246Sdes pfs_vncache = pvd; 18575295Sdes mtx_unlock(&pfs_vncache_mutex); 186105077Smckusick (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; 18799566Sjeff vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 18875295Sdes return (0); 18975295Sdes} 19075295Sdes 19175295Sdes/* 19275295Sdes * Free a vnode 19375295Sdes */ 19475295Sdesint 19575295Sdespfs_vncache_free(struct vnode *vp) 19675295Sdes{ 19777998Sdes struct pfs_vdata *pvd; 19888234Sdillon 19975295Sdes mtx_lock(&pfs_vncache_mutex); 20084246Sdes pvd = (struct pfs_vdata *)vp->v_data; 20184246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 20284246Sdes if (pvd->pvd_next) 20384246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 20484246Sdes if (pvd->pvd_prev) 20584246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 20677998Sdes else 20784246Sdes pfs_vncache = pvd->pvd_next; 20877998Sdes mtx_unlock(&pfs_vncache_mutex); 20984246Sdes 21084246Sdes --pfs_vncache_entries; 21177998Sdes FREE(pvd, M_PFSVNCACHE); 21275295Sdes vp->v_data = NULL; 21375295Sdes return (0); 21475295Sdes} 21584246Sdes 21684246Sdes/* 21784246Sdes * Free all vnodes associated with a defunct process 218139896Srwatson * 219139896Srwatson * XXXRW: It is unfortunate that pfs_exit() always acquires and releases two 220139896Srwatson * mutexes (one of which is Giant) for every process exit, even if procfs 221139896Srwatson * isn't mounted. 22284246Sdes */ 22384246Sdesstatic void 224112564Sjhbpfs_exit(void *arg, struct proc *p) 22584246Sdes{ 226133776Sdes struct pfs_vdata *pvd; 227133776Sdes struct vnode *vnp; 22884246Sdes 229126975Sgreen mtx_lock(&Giant); 23084246Sdes /* 231133776Sdes * This is extremely inefficient due to the fact that vgone() not 232133776Sdes * only indirectly modifies the vnode cache, but may also sleep. 233133776Sdes * We can neither hold pfs_vncache_mutex across a vgone() call, 234133776Sdes * nor make any assumptions about the state of the cache after 235133776Sdes * vgone() returns. In consequence, we must start over after 236133776Sdes * every vgone() call, and keep trying until we manage to traverse 237133776Sdes * the entire cache. 238133776Sdes * 239133776Sdes * The only way to improve this situation is to change the data 240133776Sdes * structure used to implement the cache. An obvious choice in 241133776Sdes * this particular case would be a BST sorted by PID. 24284246Sdes */ 243133776Sdes mtx_lock(&pfs_vncache_mutex); 244133776Sdes pvd = pfs_vncache; 245133776Sdes while (pvd != NULL) { 246133776Sdes if (pvd->pvd_pid == p->p_pid) { 247133776Sdes vnp = pvd->pvd_vnode; 248133776Sdes mtx_unlock(&pfs_vncache_mutex); 249133776Sdes vgone(vnp); 250133776Sdes mtx_lock(&pfs_vncache_mutex); 251133776Sdes pvd = pfs_vncache; 252133776Sdes } else { 253133776Sdes pvd = pvd->pvd_next; 25484246Sdes } 25584246Sdes } 25684246Sdes mtx_unlock(&pfs_vncache_mutex); 257126975Sgreen mtx_unlock(&Giant); 25884246Sdes} 25984386Sdes 26084386Sdes/* 26184386Sdes * Disable a pseudofs node, and free all vnodes associated with it 26284386Sdes */ 26384386Sdesint 26484386Sdespfs_disable(struct pfs_node *pn) 26584386Sdes{ 266133776Sdes struct pfs_vdata *pvd; 267133776Sdes struct vnode *vnp; 26897940Sdes 26984386Sdes if (pn->pn_flags & PFS_DISABLED) 27084386Sdes return (0); 271133776Sdes pn->pn_flags |= PFS_DISABLED; 272133776Sdes /* XXX see comment above nearly identical code in pfs_exit() */ 27384386Sdes mtx_lock(&pfs_vncache_mutex); 274133776Sdes pvd = pfs_vncache; 275133776Sdes while (pvd != NULL) { 276133776Sdes if (pvd->pvd_pn == pn) { 277133776Sdes vnp = pvd->pvd_vnode; 278133776Sdes mtx_unlock(&pfs_vncache_mutex); 279133776Sdes vgone(vnp); 280133776Sdes mtx_lock(&pfs_vncache_mutex); 281133776Sdes pvd = pfs_vncache; 282133776Sdes } else { 283133776Sdes pvd = pvd->pvd_next; 28484386Sdes } 28584386Sdes } 28684386Sdes mtx_unlock(&pfs_vncache_mutex); 28784386Sdes return (0); 28884386Sdes} 28984386Sdes 29084386Sdes/* 29184386Sdes * Re-enable a disabled pseudofs node 29284386Sdes */ 29384386Sdesint 29484386Sdespfs_enable(struct pfs_node *pn) 29584386Sdes{ 29684386Sdes pn->pn_flags &= ~PFS_DISABLED; 29784386Sdes return (0); 29884386Sdes} 299