pseudofs_vncache.c revision 111119
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 111119 2003-02-19 05:47:46Z imp $ 2975295Sdes */ 3075295Sdes 3175295Sdes#include <sys/param.h> 3275295Sdes#include <sys/kernel.h> 3375295Sdes#include <sys/systm.h> 3478073Sdes#include <sys/lock.h> 3575295Sdes#include <sys/malloc.h> 3677965Sdes#include <sys/mutex.h> 3784246Sdes#include <sys/proc.h> 3875295Sdes#include <sys/sysctl.h> 3975295Sdes#include <sys/vnode.h> 4075295Sdes 4175295Sdes#include <fs/pseudofs/pseudofs.h> 4275295Sdes#include <fs/pseudofs/pseudofs_internal.h> 4375295Sdes 4477998Sdesstatic MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 4575295Sdes 4675295Sdesstatic struct mtx pfs_vncache_mutex; 4789071Smsmithstatic struct pfs_vdata *pfs_vncache; 4884246Sdesstatic void pfs_exit(struct proc *p); 4975295Sdes 5075295SdesSYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 5175295Sdes "pseudofs vnode cache"); 5275295Sdes 5384246Sdesstatic int pfs_vncache_entries; 5484246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 5584246Sdes &pfs_vncache_entries, 0, 5684246Sdes "number of entries in the vnode cache"); 5784246Sdes 5884246Sdesstatic int pfs_vncache_maxentries; 5984246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 6084246Sdes &pfs_vncache_maxentries, 0, 6184246Sdes "highest number of entries in the vnode cache"); 6284246Sdes 6375295Sdesstatic int pfs_vncache_hits; 6484246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 6584246Sdes &pfs_vncache_hits, 0, 6675295Sdes "number of cache hits since initialization"); 6775295Sdes 6875295Sdesstatic int pfs_vncache_misses; 6984246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 7084246Sdes &pfs_vncache_misses, 0, 7175295Sdes "number of cache misses since initialization"); 7275295Sdes 7375295Sdesextern vop_t **pfs_vnodeop_p; 7475295Sdes 7575295Sdes/* 7675295Sdes * Initialize vnode cache 7775295Sdes */ 7875295Sdesvoid 7975295Sdespfs_vncache_load(void) 8075295Sdes{ 8193818Sjhb mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, 8293818Sjhb MTX_DEF | MTX_RECURSE); 8384246Sdes /* XXX at_exit() can fail with ENOMEN */ 8484246Sdes at_exit(pfs_exit); 8575295Sdes} 8675295Sdes 8775295Sdes/* 8875295Sdes * Tear down vnode cache 8975295Sdes */ 9075295Sdesvoid 9175295Sdespfs_vncache_unload(void) 9275295Sdes{ 9384246Sdes rm_at_exit(pfs_exit); 9484386Sdes if (pfs_vncache_entries != 0) 9584386Sdes printf("pfs_vncache_unload(): %d entries remaining\n", 9684386Sdes pfs_vncache_entries); 9775295Sdes mtx_destroy(&pfs_vncache_mutex); 9875295Sdes} 9975295Sdes 10075295Sdes/* 10175295Sdes * Allocate a vnode 10275295Sdes */ 10375295Sdesint 10477998Sdespfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 10577998Sdes struct pfs_node *pn, pid_t pid) 10675295Sdes{ 10777998Sdes struct pfs_vdata *pvd; 10875295Sdes int error; 10988234Sdillon 11088234Sdillon /* 11197940Sdes * See if the vnode is in the cache. 11288234Sdillon * XXX linear search is not very efficient. 11388234Sdillon */ 11475295Sdes mtx_lock(&pfs_vncache_mutex); 11584246Sdes for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 116109969Stjr if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 117109969Stjr pvd->pvd_vnode->v_mount == mp) { 11884246Sdes if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 11975295Sdes ++pfs_vncache_hits; 12084246Sdes *vpp = pvd->pvd_vnode; 12175295Sdes mtx_unlock(&pfs_vncache_mutex); 12288234Sdillon /* XXX see comment at top of pfs_lookup() */ 12388234Sdillon cache_purge(*vpp); 12499566Sjeff vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 12599566Sjeff curthread); 12675295Sdes return (0); 12775295Sdes } 12877998Sdes /* XXX if this can happen, we're in trouble */ 12977998Sdes break; 13077998Sdes } 13177998Sdes } 13277998Sdes mtx_unlock(&pfs_vncache_mutex); 13375295Sdes ++pfs_vncache_misses; 13475295Sdes 13575295Sdes /* nope, get a new one */ 136111119Simp MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 13784246Sdes if (++pfs_vncache_entries > pfs_vncache_maxentries) 13884246Sdes pfs_vncache_maxentries = pfs_vncache_entries; 139103314Snjl error = getnewvnode("pseudofs", mp, pfs_vnodeop_p, vpp); 140105165Sphk if (error) { 141105165Sphk FREE(pvd, M_PFSVNCACHE); 14275295Sdes return (error); 143105165Sphk } 14477998Sdes pvd->pvd_pn = pn; 14577998Sdes pvd->pvd_pid = pid; 14677998Sdes (*vpp)->v_data = pvd; 14775295Sdes switch (pn->pn_type) { 14875295Sdes case pfstype_root: 149101308Sjeff (*vpp)->v_vflag = VV_ROOT; 15075295Sdes#if 0 15175295Sdes printf("root vnode allocated\n"); 15275295Sdes#endif 15384246Sdes /* fall through */ 15475295Sdes case pfstype_dir: 15575295Sdes case pfstype_this: 15675295Sdes case pfstype_parent: 15777998Sdes case pfstype_procdir: 15875295Sdes (*vpp)->v_type = VDIR; 15975295Sdes break; 16075295Sdes case pfstype_file: 16175295Sdes (*vpp)->v_type = VREG; 16275295Sdes break; 16375295Sdes case pfstype_symlink: 16475295Sdes (*vpp)->v_type = VLNK; 16575295Sdes break; 16677998Sdes case pfstype_none: 16777998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 16875295Sdes default: 16975295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 17075295Sdes } 171103314Snjl /* 172103314Snjl * Propagate flag through to vnode so users know it can change 173103314Snjl * if the process changes (i.e. execve) 174103314Snjl */ 175103314Snjl if ((pn->pn_flags & PFS_PROCDEP) != 0) 176103314Snjl (*vpp)->v_vflag |= VV_PROCDEP; 17784246Sdes pvd->pvd_vnode = *vpp; 17877998Sdes mtx_lock(&pfs_vncache_mutex); 17984246Sdes pvd->pvd_prev = NULL; 18084246Sdes pvd->pvd_next = pfs_vncache; 18184246Sdes if (pvd->pvd_next) 18284246Sdes pvd->pvd_next->pvd_prev = pvd; 18384246Sdes pfs_vncache = pvd; 18475295Sdes mtx_unlock(&pfs_vncache_mutex); 185105077Smckusick (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; 18699566Sjeff vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 18775295Sdes return (0); 18875295Sdes} 18975295Sdes 19075295Sdes/* 19175295Sdes * Free a vnode 19275295Sdes */ 19375295Sdesint 19475295Sdespfs_vncache_free(struct vnode *vp) 19575295Sdes{ 19677998Sdes struct pfs_vdata *pvd; 19788234Sdillon 19888234Sdillon cache_purge(vp); 19997940Sdes 20075295Sdes mtx_lock(&pfs_vncache_mutex); 20184246Sdes pvd = (struct pfs_vdata *)vp->v_data; 20284246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 20384246Sdes if (pvd->pvd_next) 20484246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 20584246Sdes if (pvd->pvd_prev) 20684246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 20777998Sdes else 20884246Sdes pfs_vncache = pvd->pvd_next; 20977998Sdes mtx_unlock(&pfs_vncache_mutex); 21084246Sdes 21184246Sdes --pfs_vncache_entries; 21277998Sdes FREE(pvd, M_PFSVNCACHE); 21375295Sdes vp->v_data = NULL; 21475295Sdes return (0); 21575295Sdes} 21684246Sdes 21784246Sdes/* 21884246Sdes * Free all vnodes associated with a defunct process 21984246Sdes */ 22084246Sdesstatic void 22184246Sdespfs_exit(struct proc *p) 22284246Sdes{ 22384246Sdes struct pfs_vdata *pvd, *prev; 22484246Sdes 22584246Sdes mtx_lock(&pfs_vncache_mutex); 22684246Sdes /* 22784246Sdes * The double loop is necessary because vgone() indirectly 22884246Sdes * calls pfs_vncache_free() which frees pvd, so we have to 22984246Sdes * backtrace one step every time we free a vnode. 23084246Sdes */ 23184246Sdes /* XXX linear search... not very efficient */ 23284246Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 23384246Sdes while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 23484246Sdes prev = pvd->pvd_prev; 23584246Sdes vgone(pvd->pvd_vnode); 23684246Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 23784246Sdes } 23888868Stanimura if (pvd == NULL) 23988868Stanimura break; 24084246Sdes } 24184246Sdes mtx_unlock(&pfs_vncache_mutex); 24284246Sdes} 24384386Sdes 24484386Sdes/* 24584386Sdes * Disable a pseudofs node, and free all vnodes associated with it 24684386Sdes */ 24784386Sdesint 24884386Sdespfs_disable(struct pfs_node *pn) 24984386Sdes{ 25084386Sdes struct pfs_vdata *pvd, *prev; 25197940Sdes 25284386Sdes if (pn->pn_flags & PFS_DISABLED) 25384386Sdes return (0); 25484386Sdes mtx_lock(&pfs_vncache_mutex); 25584386Sdes pn->pn_flags |= PFS_DISABLED; 25684386Sdes /* see the comment about the double loop in pfs_exit() */ 25784386Sdes /* XXX linear search... not very efficient */ 25884386Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 25984386Sdes while (pvd != NULL && pvd->pvd_pn == pn) { 26084386Sdes prev = pvd->pvd_prev; 26184386Sdes vgone(pvd->pvd_vnode); 26284386Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 26384386Sdes } 26488868Stanimura if (pvd == NULL) 26588868Stanimura break; 26684386Sdes } 26784386Sdes mtx_unlock(&pfs_vncache_mutex); 26884386Sdes return (0); 26984386Sdes} 27084386Sdes 27184386Sdes/* 27284386Sdes * Re-enable a disabled pseudofs node 27384386Sdes */ 27484386Sdesint 27584386Sdespfs_enable(struct pfs_node *pn) 27684386Sdes{ 27784386Sdes pn->pn_flags &= ~PFS_DISABLED; 27884386Sdes return (0); 27984386Sdes} 280