175295Sdes/*- 2230132Suqs * 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 29143592Sdes#include <sys/cdefs.h> 30143592Sdes__FBSDID("$FreeBSD: stable/11/sys/fs/pseudofs/pseudofs_vncache.c 312248 2017-01-16 00:43:57Z kib $"); 31143592Sdes 32143592Sdes#include "opt_pseudofs.h" 33143592Sdes 3475295Sdes#include <sys/param.h> 3575295Sdes#include <sys/kernel.h> 3675295Sdes#include <sys/systm.h> 37112564Sjhb#include <sys/eventhandler.h> 3878073Sdes#include <sys/lock.h> 3975295Sdes#include <sys/malloc.h> 4077965Sdes#include <sys/mutex.h> 4184246Sdes#include <sys/proc.h> 4275295Sdes#include <sys/sysctl.h> 4375295Sdes#include <sys/vnode.h> 4475295Sdes 4575295Sdes#include <fs/pseudofs/pseudofs.h> 4675295Sdes#include <fs/pseudofs/pseudofs_internal.h> 4775295Sdes 4877998Sdesstatic MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 4975295Sdes 5075295Sdesstatic struct mtx pfs_vncache_mutex; 5189071Smsmithstatic struct pfs_vdata *pfs_vncache; 52112564Sjhbstatic eventhandler_tag pfs_exit_tag; 53112564Sjhbstatic void pfs_exit(void *arg, struct proc *p); 54312248Skibstatic void pfs_purge_locked(struct pfs_node *pn, bool force); 5575295Sdes 56227309Sedstatic SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 5775295Sdes "pseudofs vnode cache"); 5875295Sdes 5984246Sdesstatic int pfs_vncache_entries; 6084246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 6184246Sdes &pfs_vncache_entries, 0, 6284246Sdes "number of entries in the vnode cache"); 6384246Sdes 6484246Sdesstatic int pfs_vncache_maxentries; 6584246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 6684246Sdes &pfs_vncache_maxentries, 0, 6784246Sdes "highest number of entries in the vnode cache"); 6884246Sdes 6975295Sdesstatic int pfs_vncache_hits; 7084246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 7184246Sdes &pfs_vncache_hits, 0, 7275295Sdes "number of cache hits since initialization"); 7375295Sdes 7475295Sdesstatic int pfs_vncache_misses; 7584246SdesSYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 7684246Sdes &pfs_vncache_misses, 0, 7775295Sdes "number of cache misses since initialization"); 7875295Sdes 79138290Sphkextern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ 8075295Sdes 8175295Sdes/* 8275295Sdes * Initialize vnode cache 8375295Sdes */ 8475295Sdesvoid 8575295Sdespfs_vncache_load(void) 8675295Sdes{ 87168720Sdes 88168720Sdes mtx_init(&pfs_vncache_mutex, "pfs_vncache", NULL, MTX_DEF); 89112564Sjhb pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 90112564Sjhb EVENTHANDLER_PRI_ANY); 9175295Sdes} 9275295Sdes 9375295Sdes/* 9475295Sdes * Tear down vnode cache 9575295Sdes */ 9675295Sdesvoid 9775295Sdespfs_vncache_unload(void) 9875295Sdes{ 99168720Sdes 100112564Sjhb EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 101312248Skib mtx_lock(&pfs_vncache_mutex); 102312248Skib pfs_purge_locked(NULL, true); 103312248Skib mtx_unlock(&pfs_vncache_mutex); 104168720Sdes KASSERT(pfs_vncache_entries == 0, 105168720Sdes ("%d vncache entries remaining", pfs_vncache_entries)); 10675295Sdes mtx_destroy(&pfs_vncache_mutex); 10775295Sdes} 10875295Sdes 10975295Sdes/* 11075295Sdes * Allocate a vnode 11175295Sdes */ 11275295Sdesint 11377998Sdespfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 11477998Sdes struct pfs_node *pn, pid_t pid) 11575295Sdes{ 116186560Skib struct pfs_vdata *pvd, *pvd2; 117165737Sjhb struct vnode *vp; 11875295Sdes int error; 11988234Sdillon 12088234Sdillon /* 12197940Sdes * See if the vnode is in the cache. 12288234Sdillon * XXX linear search is not very efficient. 12388234Sdillon */ 124165737Sjhbretry: 12575295Sdes mtx_lock(&pfs_vncache_mutex); 12684246Sdes for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 127109969Stjr if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 128109969Stjr pvd->pvd_vnode->v_mount == mp) { 129165737Sjhb vp = pvd->pvd_vnode; 130165737Sjhb VI_LOCK(vp); 131165737Sjhb mtx_unlock(&pfs_vncache_mutex); 132165737Sjhb if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { 13375295Sdes ++pfs_vncache_hits; 134165737Sjhb *vpp = vp; 135168637Sdes /* 136168637Sdes * Some callers cache_enter(vp) later, so 137168637Sdes * we have to make sure it's not in the 138168637Sdes * VFS cache so it doesn't get entered 139168637Sdes * twice. A better solution would be to 140168637Sdes * make pfs_vncache_alloc() responsible 141168637Sdes * for entering the vnode in the VFS 142168637Sdes * cache. 143168637Sdes */ 144168637Sdes cache_purge(vp); 14575295Sdes return (0); 14675295Sdes } 147165737Sjhb goto retry; 14877998Sdes } 14977998Sdes } 15077998Sdes mtx_unlock(&pfs_vncache_mutex); 15175295Sdes 15275295Sdes /* nope, get a new one */ 153184205Sdes pvd = malloc(sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 154186565Skib pvd->pvd_next = pvd->pvd_prev = NULL; 155138290Sphk error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); 156105165Sphk if (error) { 157184205Sdes free(pvd, M_PFSVNCACHE); 15875295Sdes return (error); 159105165Sphk } 16077998Sdes pvd->pvd_pn = pn; 16177998Sdes pvd->pvd_pid = pid; 16277998Sdes (*vpp)->v_data = pvd; 16375295Sdes switch (pn->pn_type) { 16475295Sdes case pfstype_root: 165101308Sjeff (*vpp)->v_vflag = VV_ROOT; 16675295Sdes#if 0 16775295Sdes printf("root vnode allocated\n"); 16875295Sdes#endif 16984246Sdes /* fall through */ 17075295Sdes case pfstype_dir: 17175295Sdes case pfstype_this: 17275295Sdes case pfstype_parent: 17377998Sdes case pfstype_procdir: 17475295Sdes (*vpp)->v_type = VDIR; 17575295Sdes break; 17675295Sdes case pfstype_file: 17775295Sdes (*vpp)->v_type = VREG; 17875295Sdes break; 17975295Sdes case pfstype_symlink: 18075295Sdes (*vpp)->v_type = VLNK; 18175295Sdes break; 18277998Sdes case pfstype_none: 18377998Sdes KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 18475295Sdes default: 18575295Sdes panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 18675295Sdes } 187103314Snjl /* 188103314Snjl * Propagate flag through to vnode so users know it can change 189103314Snjl * if the process changes (i.e. execve) 190103314Snjl */ 191103314Snjl if ((pn->pn_flags & PFS_PROCDEP) != 0) 192103314Snjl (*vpp)->v_vflag |= VV_PROCDEP; 19384246Sdes pvd->pvd_vnode = *vpp; 194211531Sjhb vn_lock(*vpp, LK_EXCLUSIVE | LK_RETRY); 195176519Sattilio VN_LOCK_AREC(*vpp); 196167497Stegge error = insmntque(*vpp, mp); 197167497Stegge if (error != 0) { 198196920Skib free(pvd, M_PFSVNCACHE); 199167497Stegge *vpp = NULLVP; 200167497Stegge return (error); 201167497Stegge } 202186560Skibretry2: 20377998Sdes mtx_lock(&pfs_vncache_mutex); 204186560Skib /* 205186560Skib * Other thread may race with us, creating the entry we are 206186560Skib * going to insert into the cache. Recheck after 207186560Skib * pfs_vncache_mutex is reacquired. 208186560Skib */ 209186560Skib for (pvd2 = pfs_vncache; pvd2; pvd2 = pvd2->pvd_next) { 210186560Skib if (pvd2->pvd_pn == pn && pvd2->pvd_pid == pid && 211186560Skib pvd2->pvd_vnode->v_mount == mp) { 212186560Skib vp = pvd2->pvd_vnode; 213186560Skib VI_LOCK(vp); 214186560Skib mtx_unlock(&pfs_vncache_mutex); 215186560Skib if (vget(vp, LK_EXCLUSIVE | LK_INTERLOCK, curthread) == 0) { 216186560Skib ++pfs_vncache_hits; 217186560Skib vgone(*vpp); 218186981Smarcus vput(*vpp); 219186560Skib *vpp = vp; 220186560Skib cache_purge(vp); 221186560Skib return (0); 222186560Skib } 223186560Skib goto retry2; 224186560Skib } 225186560Skib } 226186560Skib ++pfs_vncache_misses; 227186560Skib if (++pfs_vncache_entries > pfs_vncache_maxentries) 228186560Skib pfs_vncache_maxentries = pfs_vncache_entries; 22984246Sdes pvd->pvd_prev = NULL; 23084246Sdes pvd->pvd_next = pfs_vncache; 23184246Sdes if (pvd->pvd_next) 23284246Sdes pvd->pvd_next->pvd_prev = pvd; 23384246Sdes pfs_vncache = pvd; 23475295Sdes mtx_unlock(&pfs_vncache_mutex); 23575295Sdes return (0); 23675295Sdes} 23775295Sdes 23875295Sdes/* 23975295Sdes * Free a vnode 24075295Sdes */ 24175295Sdesint 24275295Sdespfs_vncache_free(struct vnode *vp) 24375295Sdes{ 24477998Sdes struct pfs_vdata *pvd; 24588234Sdillon 24675295Sdes mtx_lock(&pfs_vncache_mutex); 24784246Sdes pvd = (struct pfs_vdata *)vp->v_data; 24884246Sdes KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 24984246Sdes if (pvd->pvd_next) 25084246Sdes pvd->pvd_next->pvd_prev = pvd->pvd_prev; 251196921Skib if (pvd->pvd_prev) { 25284246Sdes pvd->pvd_prev->pvd_next = pvd->pvd_next; 253196921Skib --pfs_vncache_entries; 254196921Skib } else if (pfs_vncache == pvd) { 25584246Sdes pfs_vncache = pvd->pvd_next; 256196921Skib --pfs_vncache_entries; 257196921Skib } 25877998Sdes mtx_unlock(&pfs_vncache_mutex); 25984246Sdes 260184205Sdes free(pvd, M_PFSVNCACHE); 26175295Sdes vp->v_data = NULL; 26275295Sdes return (0); 26375295Sdes} 26484246Sdes 26584246Sdes/* 266168764Sdes * Purge the cache of dead entries 267139896Srwatson * 268168637Sdes * This is extremely inefficient due to the fact that vgone() not only 269168637Sdes * indirectly modifies the vnode cache, but may also sleep. We can 270168637Sdes * neither hold pfs_vncache_mutex across a vgone() call, nor make any 271168637Sdes * assumptions about the state of the cache after vgone() returns. In 272168637Sdes * consequence, we must start over after every vgone() call, and keep 273168637Sdes * trying until we manage to traverse the entire cache. 274168637Sdes * 275168637Sdes * The only way to improve this situation is to change the data structure 276168637Sdes * used to implement the cache. 27784246Sdes */ 278193556Sdesstatic void 279312248Skibpfs_purge_locked(struct pfs_node *pn, bool force) 28084246Sdes{ 281133776Sdes struct pfs_vdata *pvd; 282133776Sdes struct vnode *vnp; 28384246Sdes 284193556Sdes mtx_assert(&pfs_vncache_mutex, MA_OWNED); 285133776Sdes pvd = pfs_vncache; 286133776Sdes while (pvd != NULL) { 287312248Skib if (force || pvd->pvd_dead || 288312248Skib (pn != NULL && pvd->pvd_pn == pn)) { 289133776Sdes vnp = pvd->pvd_vnode; 290147809Sjeff vhold(vnp); 291133776Sdes mtx_unlock(&pfs_vncache_mutex); 292175294Sattilio VOP_LOCK(vnp, LK_EXCLUSIVE); 293133776Sdes vgone(vnp); 294175294Sattilio VOP_UNLOCK(vnp, 0); 295193556Sdes mtx_lock(&pfs_vncache_mutex); 296147809Sjeff vdrop(vnp); 297133776Sdes pvd = pfs_vncache; 298133776Sdes } else { 299133776Sdes pvd = pvd->pvd_next; 30084246Sdes } 30184246Sdes } 302193556Sdes} 303193556Sdes 304193556Sdesvoid 305193556Sdespfs_purge(struct pfs_node *pn) 306193556Sdes{ 307193556Sdes 308193556Sdes mtx_lock(&pfs_vncache_mutex); 309312248Skib pfs_purge_locked(pn, false); 31084246Sdes mtx_unlock(&pfs_vncache_mutex); 311168637Sdes} 312168637Sdes 313168637Sdes/* 314168637Sdes * Free all vnodes associated with a defunct process 315168637Sdes */ 316168637Sdesstatic void 317168637Sdespfs_exit(void *arg, struct proc *p) 318168637Sdes{ 319168637Sdes struct pfs_vdata *pvd; 320168637Sdes int dead; 321168637Sdes 322168637Sdes if (pfs_vncache == NULL) 323168637Sdes return; 324168637Sdes mtx_lock(&pfs_vncache_mutex); 325168637Sdes for (pvd = pfs_vncache, dead = 0; pvd != NULL; pvd = pvd->pvd_next) 326168637Sdes if (pvd->pvd_pid == p->p_pid) 327168637Sdes dead = pvd->pvd_dead = 1; 328193556Sdes if (dead) 329312248Skib pfs_purge_locked(NULL, false); 330168637Sdes mtx_unlock(&pfs_vncache_mutex); 33184246Sdes} 332