pseudofs_vncache.c revision 101308
1288253Sadrian/*- 2288253Sadrian * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav 3288253Sadrian * All rights reserved. 4288253Sadrian * 5288253Sadrian * Redistribution and use in source and binary forms, with or without 6288253Sadrian * modification, are permitted provided that the following conditions 7288253Sadrian * are met: 8288253Sadrian * 1. Redistributions of source code must retain the above copyright 9288253Sadrian * notice, this list of conditions and the following disclaimer 10288253Sadrian * in this position and unchanged. 11288253Sadrian * 2. Redistributions in binary form must reproduce the above copyright 12288253Sadrian * notice, this list of conditions and the following disclaimer in the 13288253Sadrian * documentation and/or other materials provided with the distribution. 14288253Sadrian * 3. The name of the author may not be used to endorse or promote products 15288253Sadrian * derived from this software without specific prior written permission. 16288253Sadrian * 17288253Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18288253Sadrian * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19288253Sadrian * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20288253Sadrian * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21288253Sadrian * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22288253Sadrian * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23288253Sadrian * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24288253Sadrian * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25288253Sadrian * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26288253Sadrian * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27288253Sadrian * 28288253Sadrian * $FreeBSD: head/sys/fs/pseudofs/pseudofs_vncache.c 101308 2002-08-04 10:29:36Z jeff $ 29288253Sadrian */ 30288253Sadrian 31288253Sadrian#include <sys/param.h> 32288253Sadrian#include <sys/kernel.h> 33288253Sadrian#include <sys/systm.h> 34288253Sadrian#include <sys/lock.h> 35288253Sadrian#include <sys/malloc.h> 36288253Sadrian#include <sys/mutex.h> 37288253Sadrian#include <sys/proc.h> 38288253Sadrian#include <sys/sysctl.h> 39288253Sadrian#include <sys/vnode.h> 40288253Sadrian 41288253Sadrian#include <fs/pseudofs/pseudofs.h> 42288253Sadrian#include <fs/pseudofs/pseudofs_internal.h> 43288253Sadrian 44288253Sadrianstatic MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 45288253Sadrian 46288253Sadrianstatic struct mtx pfs_vncache_mutex; 47288253Sadrianstatic struct pfs_vdata *pfs_vncache; 48288253Sadrianstatic void pfs_exit(struct proc *p); 49288253Sadrian 50288253SadrianSYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 51288253Sadrian "pseudofs vnode cache"); 52288253Sadrian 53288253Sadrianstatic int pfs_vncache_entries; 54288253SadrianSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 55288253Sadrian &pfs_vncache_entries, 0, 56288253Sadrian "number of entries in the vnode cache"); 57288253Sadrian 58288253Sadrianstatic int pfs_vncache_maxentries; 59288253SadrianSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 60288253Sadrian &pfs_vncache_maxentries, 0, 61288253Sadrian "highest number of entries in the vnode cache"); 62288253Sadrian 63288253Sadrianstatic int pfs_vncache_hits; 64288253SadrianSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 65288253Sadrian &pfs_vncache_hits, 0, 66288253Sadrian "number of cache hits since initialization"); 67288253Sadrian 68288253Sadrianstatic int pfs_vncache_misses; 69288253SadrianSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 70288253Sadrian &pfs_vncache_misses, 0, 71288253Sadrian "number of cache misses since initialization"); 72288253Sadrian 73288253Sadrianextern vop_t **pfs_vnodeop_p; 74288253Sadrian 75288253Sadrian/* 76288253Sadrian * Initialize vnode cache 77288253Sadrian */ 78288253Sadrianvoid 79288253Sadrianpfs_vncache_load(void) 80288253Sadrian{ 81288253Sadrian mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, 82288253Sadrian MTX_DEF | MTX_RECURSE); 83288253Sadrian /* XXX at_exit() can fail with ENOMEN */ 84288253Sadrian at_exit(pfs_exit); 85288253Sadrian} 86288253Sadrian 87288253Sadrian/* 88288253Sadrian * Tear down vnode cache 89288253Sadrian */ 90288253Sadrianvoid 91288253Sadrianpfs_vncache_unload(void) 92288253Sadrian{ 93288253Sadrian rm_at_exit(pfs_exit); 94288253Sadrian if (pfs_vncache_entries != 0) 95288253Sadrian printf("pfs_vncache_unload(): %d entries remaining\n", 96288253Sadrian pfs_vncache_entries); 97288253Sadrian mtx_destroy(&pfs_vncache_mutex); 98288253Sadrian} 99288253Sadrian 100288253Sadrian/* 101288253Sadrian * Allocate a vnode 102288253Sadrian */ 103288253Sadrianint 104288253Sadrianpfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 105288253Sadrian struct pfs_node *pn, pid_t pid) 106288253Sadrian{ 107288253Sadrian struct pfs_vdata *pvd; 108288253Sadrian int error; 109288253Sadrian 110288253Sadrian /* 111288253Sadrian * See if the vnode is in the cache. 112288253Sadrian * XXX linear search is not very efficient. 113288253Sadrian */ 114288253Sadrian mtx_lock(&pfs_vncache_mutex); 115288253Sadrian for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 116288253Sadrian if (pvd->pvd_pn == pn && pvd->pvd_pid == pid) { 117288253Sadrian if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 118288253Sadrian ++pfs_vncache_hits; 119288253Sadrian *vpp = pvd->pvd_vnode; 120288253Sadrian mtx_unlock(&pfs_vncache_mutex); 121288253Sadrian /* XXX see comment at top of pfs_lookup() */ 122288253Sadrian cache_purge(*vpp); 123288253Sadrian vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 124288253Sadrian curthread); 125288253Sadrian return (0); 126288253Sadrian } 127288253Sadrian /* XXX if this can happen, we're in trouble */ 128288253Sadrian break; 129288253Sadrian } 130288253Sadrian } 131288253Sadrian mtx_unlock(&pfs_vncache_mutex); 132288253Sadrian ++pfs_vncache_misses; 133288253Sadrian 134288253Sadrian /* nope, get a new one */ 135288253Sadrian MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 136288253Sadrian if (++pfs_vncache_entries > pfs_vncache_maxentries) 137288253Sadrian pfs_vncache_maxentries = pfs_vncache_entries; 138288253Sadrian error = getnewvnode(VT_PSEUDOFS, mp, pfs_vnodeop_p, vpp); 139288253Sadrian if (error) 140288253Sadrian return (error); 141288253Sadrian pvd->pvd_pn = pn; 142288253Sadrian pvd->pvd_pid = pid; 143288253Sadrian (*vpp)->v_data = pvd; 144288253Sadrian switch (pn->pn_type) { 145288253Sadrian case pfstype_root: 146288253Sadrian (*vpp)->v_vflag = VV_ROOT; 147288253Sadrian#if 0 148288253Sadrian printf("root vnode allocated\n"); 149288253Sadrian#endif 150288253Sadrian /* fall through */ 151288253Sadrian case pfstype_dir: 152288253Sadrian case pfstype_this: 153288253Sadrian case pfstype_parent: 154288253Sadrian case pfstype_procdir: 155288253Sadrian (*vpp)->v_type = VDIR; 156288253Sadrian break; 157288253Sadrian case pfstype_file: 158288253Sadrian (*vpp)->v_type = VREG; 159288253Sadrian break; 160288253Sadrian case pfstype_symlink: 161288253Sadrian (*vpp)->v_type = VLNK; 162288253Sadrian break; 163288253Sadrian case pfstype_none: 164288253Sadrian KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 165288253Sadrian default: 166288253Sadrian panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 167288253Sadrian } 168288253Sadrian pvd->pvd_vnode = *vpp; 169288253Sadrian mtx_lock(&pfs_vncache_mutex); 170288253Sadrian pvd->pvd_prev = NULL; 171288253Sadrian pvd->pvd_next = pfs_vncache; 172288253Sadrian if (pvd->pvd_next) 173288253Sadrian pvd->pvd_next->pvd_prev = pvd; 174288253Sadrian pfs_vncache = pvd; 175288253Sadrian mtx_unlock(&pfs_vncache_mutex); 176288253Sadrian (*vpp)->v_vnlock = &(*vpp)->v_lock; 177288253Sadrian lockinit((*vpp)->v_vnlock, PINOD, "pfsnod", VLKTIMEOUT, LK_CANRECURSE); 178288253Sadrian vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 179288253Sadrian return (0); 180288253Sadrian} 181288253Sadrian 182288253Sadrian/* 183288253Sadrian * Free a vnode 184288253Sadrian */ 185288253Sadrianint 186288253Sadrianpfs_vncache_free(struct vnode *vp) 187288253Sadrian{ 188288253Sadrian struct pfs_vdata *pvd; 189288253Sadrian 190288253Sadrian cache_purge(vp); 191288253Sadrian 192288253Sadrian mtx_lock(&pfs_vncache_mutex); 193288253Sadrian pvd = (struct pfs_vdata *)vp->v_data; 194288253Sadrian KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 195288253Sadrian if (pvd->pvd_next) 196288253Sadrian pvd->pvd_next->pvd_prev = pvd->pvd_prev; 197288253Sadrian if (pvd->pvd_prev) 198288253Sadrian pvd->pvd_prev->pvd_next = pvd->pvd_next; 199288253Sadrian else 200288253Sadrian pfs_vncache = pvd->pvd_next; 201288253Sadrian mtx_unlock(&pfs_vncache_mutex); 202288253Sadrian 203288253Sadrian --pfs_vncache_entries; 204288253Sadrian FREE(pvd, M_PFSVNCACHE); 205288253Sadrian vp->v_data = NULL; 206288253Sadrian return (0); 207288253Sadrian} 208288253Sadrian 209288253Sadrian/* 210288253Sadrian * Free all vnodes associated with a defunct process 211288253Sadrian */ 212288253Sadrianstatic void 213288253Sadrianpfs_exit(struct proc *p) 214288253Sadrian{ 215288253Sadrian struct pfs_vdata *pvd, *prev; 216288253Sadrian 217288253Sadrian mtx_lock(&pfs_vncache_mutex); 218288253Sadrian /* 219288253Sadrian * The double loop is necessary because vgone() indirectly 220288253Sadrian * calls pfs_vncache_free() which frees pvd, so we have to 221288253Sadrian * backtrace one step every time we free a vnode. 222288253Sadrian */ 223288253Sadrian /* XXX linear search... not very efficient */ 224288253Sadrian for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 225288253Sadrian while (pvd != NULL && pvd->pvd_pid == p->p_pid) { 226288253Sadrian prev = pvd->pvd_prev; 227288253Sadrian vgone(pvd->pvd_vnode); 228288253Sadrian pvd = prev ? prev->pvd_next : pfs_vncache; 229288253Sadrian } 230288253Sadrian if (pvd == NULL) 231288253Sadrian break; 232288253Sadrian } 233288253Sadrian mtx_unlock(&pfs_vncache_mutex); 234288253Sadrian} 235288253Sadrian 236288253Sadrian/* 237288253Sadrian * Disable a pseudofs node, and free all vnodes associated with it 238288253Sadrian */ 239288253Sadrianint 240288253Sadrianpfs_disable(struct pfs_node *pn) 241288253Sadrian{ 242288253Sadrian struct pfs_vdata *pvd, *prev; 243288253Sadrian 244288253Sadrian if (pn->pn_flags & PFS_DISABLED) 245288253Sadrian return (0); 246288253Sadrian mtx_lock(&pfs_vncache_mutex); 247288253Sadrian pn->pn_flags |= PFS_DISABLED; 248288253Sadrian /* see the comment about the double loop in pfs_exit() */ 249288253Sadrian /* XXX linear search... not very efficient */ 250288253Sadrian for (pvd = pfs_vncache; pvd != NULL; pvd = pvd->pvd_next) { 251288253Sadrian while (pvd != NULL && pvd->pvd_pn == pn) { 252288253Sadrian prev = pvd->pvd_prev; 253288253Sadrian vgone(pvd->pvd_vnode); 254288253Sadrian pvd = prev ? prev->pvd_next : pfs_vncache; 255288253Sadrian } 256288253Sadrian if (pvd == NULL) 257288253Sadrian break; 258288253Sadrian } 259288253Sadrian mtx_unlock(&pfs_vncache_mutex); 260288253Sadrian return (0); 261288253Sadrian} 262288253Sadrian 263288253Sadrian/* 264288253Sadrian * Re-enable a disabled pseudofs node 265288253Sadrian */ 266288253Sadrianint 267288253Sadrianpfs_enable(struct pfs_node *pn) 268288253Sadrian{ 269288253Sadrian pn->pn_flags &= ~PFS_DISABLED; 270288253Sadrian return (0); 271288253Sadrian} 272288253Sadrian