pseudofs_vncache.c revision 147809
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 147809 2005-07-07 07:33:10Z jeff $"); 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 5575295SdesSYSCTL_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{ 86133776Sdes mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, MTX_DEF); 87112564Sjhb pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 88112564Sjhb EVENTHANDLER_PRI_ANY); 8975295Sdes} 9075295Sdes 9175295Sdes/* 9275295Sdes * Tear down vnode cache 9375295Sdes */ 9475295Sdesvoid 9575295Sdespfs_vncache_unload(void) 9675295Sdes{ 97112564Sjhb EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 9884386Sdes if (pfs_vncache_entries != 0) 9984386Sdes printf("pfs_vncache_unload(): %d entries remaining\n", 10084386Sdes pfs_vncache_entries); 10175295Sdes mtx_destroy(&pfs_vncache_mutex); 10275295Sdes} 10375295Sdes 10475295Sdes/* 10575295Sdes * Allocate a vnode 10675295Sdes */ 10775295Sdesint 10877998Sdespfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 10977998Sdes struct pfs_node *pn, pid_t pid) 11075295Sdes{ 11177998Sdes struct pfs_vdata *pvd; 11275295Sdes int error; 11388234Sdillon 11488234Sdillon /* 11597940Sdes * See if the vnode is in the cache. 11688234Sdillon * XXX linear search is not very efficient. 11788234Sdillon */ 11875295Sdes mtx_lock(&pfs_vncache_mutex); 11984246Sdes for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 120109969Stjr if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 121109969Stjr pvd->pvd_vnode->v_mount == mp) { 12284246Sdes if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 12375295Sdes ++pfs_vncache_hits; 12484246Sdes *vpp = pvd->pvd_vnode; 12575295Sdes mtx_unlock(&pfs_vncache_mutex); 12688234Sdillon /* XXX see comment at top of pfs_lookup() */ 12788234Sdillon cache_purge(*vpp); 12899566Sjeff vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 12999566Sjeff curthread); 13075295Sdes return (0); 13175295Sdes } 13277998Sdes /* XXX if this can happen, we're in trouble */ 13377998Sdes break; 13477998Sdes } 13577998Sdes } 13677998Sdes mtx_unlock(&pfs_vncache_mutex); 13775295Sdes ++pfs_vncache_misses; 13875295Sdes 13975295Sdes /* nope, get a new one */ 140111119Simp MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 14184246Sdes if (++pfs_vncache_entries > pfs_vncache_maxentries) 14284246Sdes pfs_vncache_maxentries = pfs_vncache_entries; 143138290Sphk error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); 144105165Sphk if (error) { 145105165Sphk FREE(pvd, M_PFSVNCACHE); 14675295Sdes return (error); 147105165Sphk } 14877998Sdes pvd->pvd_pn = pn; 14977998Sdes pvd->pvd_pid = pid; 15077998Sdes (*vpp)->v_data = pvd; 15175295Sdes switch (pn->pn_type) { 15275295Sdes case pfstype_root: 153101308Sjeff (*vpp)->v_vflag = VV_ROOT; 15475295Sdes#if 0 15575295Sdes printf("root vnode allocated\n"); 15675295Sdes#endif 15784246Sdes /* fall through */ 15875295Sdes case pfstype_dir: 15975295Sdes case pfstype_this: 16075295Sdes case pfstype_parent: 16177998Sdes case pfstype_procdir: 16275295Sdes (*vpp)->v_type = VDIR; 16375295Sdes break; 16475295Sdes case pfstype_file: 16575295Sdes (*vpp)->v_type = VREG; 16675295Sdes break; 16775295Sdes case pfstype_symlink: 16875295Sdes (*vpp)->v_type = VLNK; 16975295Sdes break; 17077998Sdes case pfstype_none: 17177998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 17275295Sdes default: 17375295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 17475295Sdes } 175103314Snjl /* 176103314Snjl * Propagate flag through to vnode so users know it can change 177103314Snjl * if the process changes (i.e. execve) 178103314Snjl */ 179103314Snjl if ((pn->pn_flags & PFS_PROCDEP) != 0) 180103314Snjl (*vpp)->v_vflag |= VV_PROCDEP; 18184246Sdes pvd->pvd_vnode = *vpp; 18277998Sdes mtx_lock(&pfs_vncache_mutex); 18384246Sdes pvd->pvd_prev = NULL; 18484246Sdes pvd->pvd_next = pfs_vncache; 18584246Sdes if (pvd->pvd_next) 18684246Sdes pvd->pvd_next->pvd_prev = pvd; 18784246Sdes pfs_vncache = pvd; 18875295Sdes mtx_unlock(&pfs_vncache_mutex); 189105077Smckusick (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; 19099566Sjeff vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 19175295Sdes return (0); 19275295Sdes} 19375295Sdes 19475295Sdes/* 19575295Sdes * Free a vnode 19675295Sdes */ 19775295Sdesint 19875295Sdespfs_vncache_free(struct vnode *vp) 19975295Sdes{ 20077998Sdes struct pfs_vdata *pvd; 20188234Sdillon 20275295Sdes mtx_lock(&pfs_vncache_mutex); 20384246Sdes pvd = (struct pfs_vdata *)vp->v_data; 20484246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 20584246Sdes if (pvd->pvd_next) 20684246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 20784246Sdes if (pvd->pvd_prev) 20884246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 20977998Sdes else 21084246Sdes pfs_vncache = pvd->pvd_next; 21177998Sdes mtx_unlock(&pfs_vncache_mutex); 21284246Sdes 21384246Sdes --pfs_vncache_entries; 21477998Sdes FREE(pvd, M_PFSVNCACHE); 21575295Sdes vp->v_data = NULL; 21675295Sdes return (0); 21775295Sdes} 21884246Sdes 21984246Sdes/* 22084246Sdes * Free all vnodes associated with a defunct process 221139896Srwatson * 222139896Srwatson * XXXRW: It is unfortunate that pfs_exit() always acquires and releases two 223139896Srwatson * mutexes (one of which is Giant) for every process exit, even if procfs 224139896Srwatson * isn't mounted. 22584246Sdes */ 22684246Sdesstatic void 227112564Sjhbpfs_exit(void *arg, struct proc *p) 22884246Sdes{ 229133776Sdes struct pfs_vdata *pvd; 230133776Sdes struct vnode *vnp; 23184246Sdes 232142907Sphk if (pfs_vncache == NULL) 233142907Sphk return; 234126975Sgreen mtx_lock(&Giant); 23584246Sdes /* 236133776Sdes * This is extremely inefficient due to the fact that vgone() not 237133776Sdes * only indirectly modifies the vnode cache, but may also sleep. 238133776Sdes * We can neither hold pfs_vncache_mutex across a vgone() call, 239133776Sdes * nor make any assumptions about the state of the cache after 240133776Sdes * vgone() returns. In consequence, we must start over after 241133776Sdes * every vgone() call, and keep trying until we manage to traverse 242133776Sdes * the entire cache. 243133776Sdes * 244133776Sdes * The only way to improve this situation is to change the data 245133776Sdes * structure used to implement the cache. An obvious choice in 246133776Sdes * this particular case would be a BST sorted by PID. 24784246Sdes */ 248133776Sdes mtx_lock(&pfs_vncache_mutex); 249133776Sdes pvd = pfs_vncache; 250133776Sdes while (pvd != NULL) { 251133776Sdes if (pvd->pvd_pid == p->p_pid) { 252133776Sdes vnp = pvd->pvd_vnode; 253147809Sjeff vhold(vnp); 254133776Sdes mtx_unlock(&pfs_vncache_mutex); 255143513Sjeff VOP_LOCK(vnp, LK_EXCLUSIVE, curthread); 256133776Sdes vgone(vnp); 257143513Sjeff VOP_UNLOCK(vnp, 0, curthread); 258147809Sjeff vdrop(vnp); 259133776Sdes mtx_lock(&pfs_vncache_mutex); 260133776Sdes pvd = pfs_vncache; 261133776Sdes } else { 262133776Sdes pvd = pvd->pvd_next; 26384246Sdes } 26484246Sdes } 26584246Sdes mtx_unlock(&pfs_vncache_mutex); 266126975Sgreen mtx_unlock(&Giant); 26784246Sdes} 26884386Sdes 26984386Sdes/* 27084386Sdes * Disable a pseudofs node, and free all vnodes associated with it 27184386Sdes */ 27284386Sdesint 27384386Sdespfs_disable(struct pfs_node *pn) 27484386Sdes{ 275133776Sdes struct pfs_vdata *pvd; 276133776Sdes struct vnode *vnp; 27797940Sdes 27884386Sdes if (pn->pn_flags & PFS_DISABLED) 27984386Sdes return (0); 280133776Sdes pn->pn_flags |= PFS_DISABLED; 281133776Sdes /* XXX see comment above nearly identical code in pfs_exit() */ 28284386Sdes mtx_lock(&pfs_vncache_mutex); 283133776Sdes pvd = pfs_vncache; 284133776Sdes while (pvd != NULL) { 285133776Sdes if (pvd->pvd_pn == pn) { 286133776Sdes vnp = pvd->pvd_vnode; 287147809Sjeff vhold(vnp); 288133776Sdes mtx_unlock(&pfs_vncache_mutex); 289143513Sjeff VOP_LOCK(vnp, LK_EXCLUSIVE, curthread); 290133776Sdes vgone(vnp); 291143513Sjeff VOP_UNLOCK(vnp, 0, curthread); 292147809Sjeff vdrop(vnp); 293133776Sdes mtx_lock(&pfs_vncache_mutex); 294133776Sdes pvd = pfs_vncache; 295133776Sdes } else { 296133776Sdes pvd = pvd->pvd_next; 29784386Sdes } 29884386Sdes } 29984386Sdes mtx_unlock(&pfs_vncache_mutex); 30084386Sdes return (0); 30184386Sdes} 30284386Sdes 30384386Sdes/* 30484386Sdes * Re-enable a disabled pseudofs node 30584386Sdes */ 30684386Sdesint 30784386Sdespfs_enable(struct pfs_node *pn) 30884386Sdes{ 30984386Sdes pn->pn_flags &= ~PFS_DISABLED; 31084386Sdes return (0); 31184386Sdes} 312