pseudofs_vncache.c revision 97940
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 97940 2002-06-06 16:59:24Z des $ 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) { 11677998Sdes if (pvd->pvd_pn == pn && pvd->pvd_pid == pid) { 11784246Sdes if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 11875295Sdes ++pfs_vncache_hits; 11984246Sdes *vpp = pvd->pvd_vnode; 12075295Sdes mtx_unlock(&pfs_vncache_mutex); 12188234Sdillon /* XXX see comment at top of pfs_lookup() */ 12288234Sdillon cache_purge(*vpp); 12375295Sdes return (0); 12475295Sdes } 12577998Sdes /* XXX if this can happen, we're in trouble */ 12677998Sdes break; 12777998Sdes } 12877998Sdes } 12977998Sdes mtx_unlock(&pfs_vncache_mutex); 13075295Sdes ++pfs_vncache_misses; 13175295Sdes 13275295Sdes /* nope, get a new one */ 13377998Sdes MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 13484246Sdes if (++pfs_vncache_entries > pfs_vncache_maxentries) 13584246Sdes pfs_vncache_maxentries = pfs_vncache_entries; 13675295Sdes error = getnewvnode(VT_PSEUDOFS, mp, pfs_vnodeop_p, vpp); 13777998Sdes if (error) 13875295Sdes return (error); 13977998Sdes pvd->pvd_pn = pn; 14077998Sdes pvd->pvd_pid = pid; 14177998Sdes (*vpp)->v_data = pvd; 14275295Sdes switch (pn->pn_type) { 14375295Sdes case pfstype_root: 14475295Sdes (*vpp)->v_flag = VROOT; 14575295Sdes#if 0 14675295Sdes printf("root vnode allocated\n"); 14775295Sdes#endif 14884246Sdes /* fall through */ 14975295Sdes case pfstype_dir: 15075295Sdes case pfstype_this: 15175295Sdes case pfstype_parent: 15277998Sdes case pfstype_procdir: 15375295Sdes (*vpp)->v_type = VDIR; 15475295Sdes break; 15575295Sdes case pfstype_file: 15675295Sdes (*vpp)->v_type = VREG; 15775295Sdes break; 15875295Sdes case pfstype_symlink: 15975295Sdes (*vpp)->v_type = VLNK; 16075295Sdes break; 16177998Sdes case pfstype_none: 16277998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 16375295Sdes default: 16475295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 16575295Sdes } 16684246Sdes pvd->pvd_vnode = *vpp; 16777998Sdes mtx_lock(&pfs_vncache_mutex); 16884246Sdes pvd->pvd_prev = NULL; 16984246Sdes pvd->pvd_next = pfs_vncache; 17084246Sdes if (pvd->pvd_next) 17184246Sdes pvd->pvd_next->pvd_prev = pvd; 17284246Sdes pfs_vncache = pvd; 17375295Sdes mtx_unlock(&pfs_vncache_mutex); 17475295Sdes return (0); 17575295Sdes} 17675295Sdes 17775295Sdes/* 17875295Sdes * Free a vnode 17975295Sdes */ 18075295Sdesint 18175295Sdespfs_vncache_free(struct vnode *vp) 18275295Sdes{ 18377998Sdes struct pfs_vdata *pvd; 18488234Sdillon 18588234Sdillon cache_purge(vp); 18697940Sdes 18775295Sdes mtx_lock(&pfs_vncache_mutex); 18884246Sdes pvd = (struct pfs_vdata *)vp->v_data; 18984246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 19084246Sdes if (pvd->pvd_next) 19184246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 19284246Sdes if (pvd->pvd_prev) 19384246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 19477998Sdes else 19584246Sdes pfs_vncache = pvd->pvd_next; 19677998Sdes mtx_unlock(&pfs_vncache_mutex); 19784246Sdes 19884246Sdes --pfs_vncache_entries; 19977998Sdes FREE(pvd, M_PFSVNCACHE); 20075295Sdes vp->v_data = NULL; 20175295Sdes return (0); 20275295Sdes} 20384246Sdes 20484246Sdes/* 20584246Sdes * Free all vnodes associated with a defunct process 20684246Sdes */ 20784246Sdesstatic void 20884246Sdespfs_exit(struct proc *p) 20984246Sdes{ 21084246Sdes struct pfs_vdata *pvd, *prev; 21184246Sdes 21284246Sdes mtx_lock(&pfs_vncache_mutex); 21384246Sdes /* 21484246Sdes * The double loop is necessary because vgone() indirectly 21584246Sdes * calls pfs_vncache_free() which frees pvd, so we have to 21684246Sdes * backtrace one step every time we free a vnode. 21784246Sdes */ 21884246Sdes /* XXX linear search... not very efficient */ 21984246Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 22084246Sdes while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 22184246Sdes prev = pvd->pvd_prev; 22284246Sdes vgone(pvd->pvd_vnode); 22384246Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 22484246Sdes } 22588868Stanimura if (pvd == NULL) 22688868Stanimura break; 22784246Sdes } 22884246Sdes mtx_unlock(&pfs_vncache_mutex); 22984246Sdes} 23084386Sdes 23184386Sdes/* 23284386Sdes * Disable a pseudofs node, and free all vnodes associated with it 23384386Sdes */ 23484386Sdesint 23584386Sdespfs_disable(struct pfs_node *pn) 23684386Sdes{ 23784386Sdes struct pfs_vdata *pvd, *prev; 23897940Sdes 23984386Sdes if (pn->pn_flags & PFS_DISABLED) 24084386Sdes return (0); 24184386Sdes mtx_lock(&pfs_vncache_mutex); 24284386Sdes pn->pn_flags |= PFS_DISABLED; 24384386Sdes /* see the comment about the double loop in pfs_exit() */ 24484386Sdes /* XXX linear search... not very efficient */ 24584386Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 24684386Sdes while (pvd != NULL && pvd->pvd_pn == pn) { 24784386Sdes prev = pvd->pvd_prev; 24884386Sdes vgone(pvd->pvd_vnode); 24984386Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 25084386Sdes } 25188868Stanimura if (pvd == NULL) 25288868Stanimura break; 25384386Sdes } 25484386Sdes mtx_unlock(&pfs_vncache_mutex); 25584386Sdes return (0); 25684386Sdes} 25784386Sdes 25884386Sdes/* 25984386Sdes * Re-enable a disabled pseudofs node 26084386Sdes */ 26184386Sdesint 26284386Sdespfs_enable(struct pfs_node *pn) 26384386Sdes{ 26484386Sdes pn->pn_flags &= ~PFS_DISABLED; 26584386Sdes return (0); 26684386Sdes} 267