pseudofs_vncache.c revision 84386
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 84386 2001-10-02 22:22:42Z 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> 3675295Sdes#include <sys/mount.h> 3777965Sdes#include <sys/mutex.h> 3884246Sdes#include <sys/proc.h> 3975295Sdes#include <sys/sbuf.h> 4075295Sdes#include <sys/sysctl.h> 4175295Sdes#include <sys/vnode.h> 4275295Sdes 4375295Sdes#include <fs/pseudofs/pseudofs.h> 4475295Sdes#include <fs/pseudofs/pseudofs_internal.h> 4575295Sdes 4677998Sdesstatic MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 4775295Sdes 4875295Sdesstatic struct mtx pfs_vncache_mutex; 4984246Sdesstruct pfs_vdata *pfs_vncache; 5084246Sdesstatic void pfs_exit(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 7575295Sdesextern vop_t **pfs_vnodeop_p; 7675295Sdes 7775295Sdes/* 7875295Sdes * Initialize vnode cache 7975295Sdes */ 8075295Sdesvoid 8175295Sdespfs_vncache_load(void) 8275295Sdes{ 8384246Sdes mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", MTX_DEF|MTX_RECURSE); 8484246Sdes /* XXX at_exit() can fail with ENOMEN */ 8584246Sdes at_exit(pfs_exit); 8675295Sdes} 8775295Sdes 8875295Sdes/* 8975295Sdes * Tear down vnode cache 9075295Sdes */ 9175295Sdesvoid 9275295Sdespfs_vncache_unload(void) 9375295Sdes{ 9484246Sdes rm_at_exit(pfs_exit); 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; 11075295Sdes 11177998Sdes /* see if the vnode is in the cache */ 11284246Sdes /* XXX linear search... not very efficient */ 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); 12075295Sdes return (0); 12175295Sdes } 12277998Sdes /* XXX if this can happen, we're in trouble */ 12377998Sdes break; 12477998Sdes } 12577998Sdes } 12677998Sdes mtx_unlock(&pfs_vncache_mutex); 12775295Sdes ++pfs_vncache_misses; 12875295Sdes 12975295Sdes /* nope, get a new one */ 13077998Sdes MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 13184246Sdes if (++pfs_vncache_entries > pfs_vncache_maxentries) 13284246Sdes pfs_vncache_maxentries = pfs_vncache_entries; 13375295Sdes error = getnewvnode(VT_PSEUDOFS, mp, pfs_vnodeop_p, vpp); 13477998Sdes if (error) 13575295Sdes return (error); 13677998Sdes pvd->pvd_pn = pn; 13777998Sdes pvd->pvd_pid = pid; 13877998Sdes (*vpp)->v_data = pvd; 13975295Sdes switch (pn->pn_type) { 14075295Sdes case pfstype_root: 14175295Sdes (*vpp)->v_flag = VROOT; 14275295Sdes#if 0 14375295Sdes printf("root vnode allocated\n"); 14475295Sdes#endif 14584246Sdes /* fall through */ 14675295Sdes case pfstype_dir: 14775295Sdes case pfstype_this: 14875295Sdes case pfstype_parent: 14977998Sdes case pfstype_procdir: 15075295Sdes (*vpp)->v_type = VDIR; 15175295Sdes break; 15275295Sdes case pfstype_file: 15375295Sdes (*vpp)->v_type = VREG; 15475295Sdes break; 15575295Sdes case pfstype_symlink: 15675295Sdes (*vpp)->v_type = VLNK; 15775295Sdes break; 15877998Sdes case pfstype_none: 15977998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 16075295Sdes default: 16175295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 16275295Sdes } 16384246Sdes pvd->pvd_vnode = *vpp; 16477998Sdes mtx_lock(&pfs_vncache_mutex); 16584246Sdes pvd->pvd_prev = NULL; 16684246Sdes pvd->pvd_next = pfs_vncache; 16784246Sdes if (pvd->pvd_next) 16884246Sdes pvd->pvd_next->pvd_prev = pvd; 16984246Sdes pfs_vncache = pvd; 17075295Sdes mtx_unlock(&pfs_vncache_mutex); 17175295Sdes return (0); 17275295Sdes} 17375295Sdes 17475295Sdes/* 17575295Sdes * Free a vnode 17675295Sdes */ 17775295Sdesint 17875295Sdespfs_vncache_free(struct vnode *vp) 17975295Sdes{ 18077998Sdes struct pfs_vdata *pvd; 18175295Sdes 18275295Sdes mtx_lock(&pfs_vncache_mutex); 18384246Sdes pvd = (struct pfs_vdata *)vp->v_data; 18484246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 18584246Sdes if (pvd->pvd_next) 18684246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 18784246Sdes if (pvd->pvd_prev) 18884246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 18977998Sdes else 19084246Sdes pfs_vncache = pvd->pvd_next; 19177998Sdes mtx_unlock(&pfs_vncache_mutex); 19284246Sdes 19384246Sdes --pfs_vncache_entries; 19477998Sdes FREE(pvd, M_PFSVNCACHE); 19575295Sdes vp->v_data = NULL; 19675295Sdes return (0); 19775295Sdes} 19884246Sdes 19984246Sdes/* 20084246Sdes * Free all vnodes associated with a defunct process 20184246Sdes */ 20284246Sdesstatic void 20384246Sdespfs_exit(struct proc *p) 20484246Sdes{ 20584246Sdes struct pfs_vdata *pvd, *prev; 20684246Sdes 20784246Sdes mtx_lock(&pfs_vncache_mutex); 20884246Sdes /* 20984246Sdes * The double loop is necessary because vgone() indirectly 21084246Sdes * calls pfs_vncache_free() which frees pvd, so we have to 21184246Sdes * backtrace one step every time we free a vnode. 21284246Sdes */ 21384246Sdes /* XXX linear search... not very efficient */ 21484246Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 21584246Sdes while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 21684246Sdes prev = pvd->pvd_prev; 21784246Sdes vgone(pvd->pvd_vnode); 21884246Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 21984246Sdes } 22084246Sdes } 22184246Sdes mtx_unlock(&pfs_vncache_mutex); 22284246Sdes} 22384386Sdes 22484386Sdes/* 22584386Sdes * Disable a pseudofs node, and free all vnodes associated with it 22684386Sdes */ 22784386Sdesint 22884386Sdespfs_disable(struct pfs_node *pn) 22984386Sdes{ 23084386Sdes struct pfs_vdata *pvd, *prev; 23184386Sdes 23284386Sdes if (pn->pn_flags & PFS_DISABLED) 23384386Sdes return (0); 23484386Sdes mtx_lock(&pfs_vncache_mutex); 23584386Sdes pn->pn_flags |= PFS_DISABLED; 23684386Sdes /* see the comment about the double loop in pfs_exit() */ 23784386Sdes /* XXX linear search... not very efficient */ 23884386Sdes for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 23984386Sdes while (pvd != NULL && pvd->pvd_pn == pn) { 24084386Sdes prev = pvd->pvd_prev; 24184386Sdes vgone(pvd->pvd_vnode); 24284386Sdes pvd = prev ? prev->pvd_next : pfs_vncache; 24384386Sdes } 24484386Sdes } 24584386Sdes mtx_unlock(&pfs_vncache_mutex); 24684386Sdes return (0); 24784386Sdes} 24884386Sdes 24984386Sdes/* 25084386Sdes * Re-enable a disabled pseudofs node 25184386Sdes */ 25284386Sdesint 25384386Sdespfs_enable(struct pfs_node *pn) 25484386Sdes{ 25584386Sdes pn->pn_flags &= ~PFS_DISABLED; 25684386Sdes return (0); 25784386Sdes} 258