pseudofs_vncache.c revision 88234
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 88234 2001-12-19 23:58:09Z dillon $ 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; 4784246Sdesstruct 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{ 8184246Sdes mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", MTX_DEF|MTX_RECURSE); 8284246Sdes /* XXX at_exit() can fail with ENOMEN */ 8384246Sdes at_exit(pfs_exit); 8475295Sdes} 8575295Sdes 8675295Sdes/* 8775295Sdes * Tear down vnode cache 8875295Sdes */ 8975295Sdesvoid 9075295Sdespfs_vncache_unload(void) 9175295Sdes{ 9284246Sdes rm_at_exit(pfs_exit); 9384386Sdes if (pfs_vncache_entries != 0) 9484386Sdes printf("pfs_vncache_unload(): %d entries remaining\n", 9584386Sdes pfs_vncache_entries); 9675295Sdes mtx_destroy(&pfs_vncache_mutex); 9775295Sdes} 9875295Sdes 9975295Sdes/* 10075295Sdes * Allocate a vnode 10175295Sdes */ 10275295Sdesint 10377998Sdespfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 10477998Sdes struct pfs_node *pn, pid_t pid) 10575295Sdes{ 10677998Sdes struct pfs_vdata *pvd; 10775295Sdes int error; 10888234Sdillon 10988234Sdillon /* 11088234Sdillon * See if the vnode is in the cache. 11188234Sdillon * XXX linear search is not very efficient. 11288234Sdillon */ 11375295Sdes mtx_lock(&pfs_vncache_mutex); 11484246Sdes for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 11577998Sdes if (pvd->pvd_pn == pn && pvd->pvd_pid == pid) { 11684246Sdes if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 11775295Sdes ++pfs_vncache_hits; 11884246Sdes *vpp = pvd->pvd_vnode; 11975295Sdes mtx_unlock(&pfs_vncache_mutex); 12088234Sdillon /* XXX see comment at top of pfs_lookup() */ 12188234Sdillon cache_purge(*vpp); 12275295Sdes return (0); 12375295Sdes } 12477998Sdes /* XXX if this can happen, we're in trouble */ 12577998Sdes break; 12677998Sdes } 12777998Sdes } 12877998Sdes mtx_unlock(&pfs_vncache_mutex); 12975295Sdes ++pfs_vncache_misses; 13075295Sdes 13175295Sdes /* nope, get a new one */ 13277998Sdes MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 13384246Sdes if (++pfs_vncache_entries > pfs_vncache_maxentries) 13484246Sdes pfs_vncache_maxentries = pfs_vncache_entries; 13575295Sdes error = getnewvnode(VT_PSEUDOFS, mp, pfs_vnodeop_p, vpp); 13677998Sdes if (error) 13775295Sdes return (error); 13877998Sdes pvd->pvd_pn = pn; 13977998Sdes pvd->pvd_pid = pid; 14077998Sdes (*vpp)->v_data = pvd; 14175295Sdes switch (pn->pn_type) { 14275295Sdes case pfstype_root: 14375295Sdes (*vpp)->v_flag = VROOT; 14475295Sdes#if 0 14575295Sdes printf("root vnode allocated\n"); 14675295Sdes#endif 14784246Sdes /* fall through */ 14875295Sdes case pfstype_dir: 14975295Sdes case pfstype_this: 15075295Sdes case pfstype_parent: 15177998Sdes case pfstype_procdir: 15275295Sdes (*vpp)->v_type = VDIR; 15375295Sdes break; 15475295Sdes case pfstype_file: 15575295Sdes (*vpp)->v_type = VREG; 15675295Sdes break; 15775295Sdes case pfstype_symlink: 15875295Sdes (*vpp)->v_type = VLNK; 15975295Sdes break; 16077998Sdes case pfstype_none: 16177998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 16275295Sdes default: 16375295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 16475295Sdes } 16584246Sdes pvd->pvd_vnode = *vpp; 16677998Sdes mtx_lock(&pfs_vncache_mutex); 16784246Sdes pvd->pvd_prev = NULL; 16884246Sdes pvd->pvd_next = pfs_vncache; 16984246Sdes if (pvd->pvd_next) 17084246Sdes pvd->pvd_next->pvd_prev = pvd; 17184246Sdes pfs_vncache = pvd; 17275295Sdes mtx_unlock(&pfs_vncache_mutex); 17375295Sdes return (0); 17475295Sdes} 17575295Sdes 17675295Sdes/* 17775295Sdes * Free a vnode 17875295Sdes */ 17975295Sdesint 18075295Sdespfs_vncache_free(struct vnode *vp) 18175295Sdes{ 18277998Sdes struct pfs_vdata *pvd; 18388234Sdillon 18488234Sdillon cache_purge(vp); 18575295Sdes 18675295Sdes mtx_lock(&pfs_vncache_mutex); 18784246Sdes pvd = (struct pfs_vdata *)vp->v_data; 18884246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 18984246Sdes if (pvd->pvd_next) 19084246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 19184246Sdes if (pvd->pvd_prev) 19284246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 19377998Sdes else 19484246Sdes pfs_vncache = pvd->pvd_next; 19577998Sdes mtx_unlock(&pfs_vncache_mutex); 19684246Sdes 19784246Sdes --pfs_vncache_entries; 19877998Sdes FREE(pvd, M_PFSVNCACHE); 19975295Sdes vp->v_data = NULL; 20075295Sdes return (0); 20175295Sdes} 20284246Sdes 20384246Sdes/* 20484246Sdes * Free all vnodes associated with a defunct process 20584246Sdes */ 20684246Sdesstatic void 20784246Sdespfs_exit(struct proc *p) 20884246Sdes{ 20984246Sdes struct pfs_vdata *pvd, *prev; 21084246Sdes 21184246Sdes mtx_lock(&pfs_vncache_mutex); 21284246Sdes /* 21384246Sdes * The double loop is necessary because vgone() indirectly 21484246Sdes * calls pfs_vncache_free() which frees pvd, so we have to 21584246Sdes * backtrace one step every time we free a vnode. 21684246Sdes */ 21784246Sdes /* XXX linear search... not very efficient */ 21884246Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 21984246Sdes while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 22084246Sdes prev = pvd->pvd_prev; 22184246Sdes vgone(pvd->pvd_vnode); 22284246Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 22384246Sdes } 22484246Sdes } 22584246Sdes mtx_unlock(&pfs_vncache_mutex); 22684246Sdes} 22784386Sdes 22884386Sdes/* 22984386Sdes * Disable a pseudofs node, and free all vnodes associated with it 23084386Sdes */ 23184386Sdesint 23284386Sdespfs_disable(struct pfs_node *pn) 23384386Sdes{ 23484386Sdes struct pfs_vdata *pvd, *prev; 23584386Sdes 23684386Sdes if (pn->pn_flags & PFS_DISABLED) 23784386Sdes return (0); 23884386Sdes mtx_lock(&pfs_vncache_mutex); 23984386Sdes pn->pn_flags |= PFS_DISABLED; 24084386Sdes /* see the comment about the double loop in pfs_exit() */ 24184386Sdes /* XXX linear search... not very efficient */ 24284386Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 24384386Sdes while (pvd != NULL && pvd->pvd_pn == pn) { 24484386Sdes prev = pvd->pvd_prev; 24584386Sdes vgone(pvd->pvd_vnode); 24684386Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 24784386Sdes } 24884386Sdes } 24984386Sdes mtx_unlock(&pfs_vncache_mutex); 25084386Sdes return (0); 25184386Sdes} 25284386Sdes 25384386Sdes/* 25484386Sdes * Re-enable a disabled pseudofs node 25584386Sdes */ 25684386Sdesint 25784386Sdespfs_enable(struct pfs_node *pn) 25884386Sdes{ 25984386Sdes pn->pn_flags &= ~PFS_DISABLED; 26084386Sdes return (0); 26184386Sdes} 262