1 /*- 2 * Copyright (c) 2001 Dag-Erling Co�dan Sm�rgrav 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer 10 * in this position and unchanged. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 * 28 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/kernel.h> 33 #include <sys/systm.h> 34 #include <sys/eventhandler.h> 35 #include <sys/lock.h> 36 #include <sys/malloc.h> 37 #include <sys/mutex.h> 38 #include <sys/proc.h> 39 #include <sys/sysctl.h> 40 #include <sys/vnode.h> 41 42 #include <fs/pseudofs/pseudofs.h> 43 #include <fs/pseudofs/pseudofs_internal.h> 44 45 static MALLOC_DEFINE(M_PFSVNCACHE, "pfs_vncache", "pseudofs vnode cache"); 46 47 static struct mtx pfs_vncache_mutex; 48 static struct pfs_vdata *pfs_vncache; 49 static eventhandler_tag pfs_exit_tag; 50 static void pfs_exit(void *arg, struct proc *p); 51 52 SYSCTL_NODE(_vfs_pfs, OID_AUTO, vncache, CTLFLAG_RW, 0, 53 "pseudofs vnode cache"); 54 55 static int pfs_vncache_entries; 56 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, entries, CTLFLAG_RD, 57 &pfs_vncache_entries, 0, 58 "number of entries in the vnode cache"); 59 60 static int pfs_vncache_maxentries; 61 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, maxentries, CTLFLAG_RD, 62 &pfs_vncache_maxentries, 0, 63 "highest number of entries in the vnode cache"); 64 65 static int pfs_vncache_hits; 66 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, hits, CTLFLAG_RD, 67 &pfs_vncache_hits, 0, 68 "number of cache hits since initialization"); 69 70 static int pfs_vncache_misses; 71 SYSCTL_INT(_vfs_pfs_vncache, OID_AUTO, misses, CTLFLAG_RD, 72 &pfs_vncache_misses, 0, 73 "number of cache misses since initialization"); 74 75 extern struct vop_vector pfs_vnodeops; /* XXX -> .h file */ 76 77 /* 78 * Initialize vnode cache 79 */ 80 void 81 pfs_vncache_load(void) 82 { 83 mtx_init(&pfs_vncache_mutex, "pseudofs_vncache", NULL, MTX_DEF); 84 pfs_exit_tag = EVENTHANDLER_REGISTER(process_exit, pfs_exit, NULL, 85 EVENTHANDLER_PRI_ANY); 86 } 87 88 /* 89 * Tear down vnode cache 90 */ 91 void 92 pfs_vncache_unload(void) 93 { 94 EVENTHANDLER_DEREGISTER(process_exit, pfs_exit_tag); 95 if (pfs_vncache_entries != 0) 96 printf("pfs_vncache_unload(): %d entries remaining\n", 97 pfs_vncache_entries); 98 mtx_destroy(&pfs_vncache_mutex); 99 } 100 101 /* 102 * Allocate a vnode 103 */ 104 int 105 pfs_vncache_alloc(struct mount *mp, struct vnode **vpp, 106 struct pfs_node *pn, pid_t pid) 107 { 108 struct pfs_vdata *pvd; 109 int error; 110 111 /* 112 * See if the vnode is in the cache. 113 * XXX linear search is not very efficient. 114 */ 115 mtx_lock(&pfs_vncache_mutex); 116 for (pvd = pfs_vncache; pvd; pvd = pvd->pvd_next) { 117 if (pvd->pvd_pn == pn && pvd->pvd_pid == pid && 118 pvd->pvd_vnode->v_mount == mp) { 119 if (vget(pvd->pvd_vnode, 0, curthread) == 0) { 120 ++pfs_vncache_hits; 121 *vpp = pvd->pvd_vnode; 122 mtx_unlock(&pfs_vncache_mutex); 123 /* XXX see comment at top of pfs_lookup() */ 124 cache_purge(*vpp); 125 vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, 126 curthread); 127 return (0); 128 } 129 /* XXX if this can happen, we're in trouble */ 130 break; 131 } 132 } 133 mtx_unlock(&pfs_vncache_mutex); 134 ++pfs_vncache_misses; 135 136 /* nope, get a new one */ 137 MALLOC(pvd, struct pfs_vdata *, sizeof *pvd, M_PFSVNCACHE, M_WAITOK); 138 if (++pfs_vncache_entries > pfs_vncache_maxentries) 139 pfs_vncache_maxentries = pfs_vncache_entries; 140 error = getnewvnode("pseudofs", mp, &pfs_vnodeops, vpp); 141 if (error) { 142 FREE(pvd, M_PFSVNCACHE); 143 return (error); 144 } 145 pvd->pvd_pn = pn; 146 pvd->pvd_pid = pid; 147 (*vpp)->v_data = pvd; 148 switch (pn->pn_type) { 149 case pfstype_root: 150 (*vpp)->v_vflag = VV_ROOT; 151 #if 0 152 printf("root vnode allocated\n"); 153 #endif 154 /* fall through */ 155 case pfstype_dir: 156 case pfstype_this: 157 case pfstype_parent: 158 case pfstype_procdir: 159 (*vpp)->v_type = VDIR; 160 break; 161 case pfstype_file: 162 (*vpp)->v_type = VREG; 163 break; 164 case pfstype_symlink: 165 (*vpp)->v_type = VLNK; 166 break; 167 case pfstype_none: 168 KASSERT(0, ("pfs_vncache_alloc called for null node\n")); 169 default: 170 panic("%s has unexpected type: %d", pn->pn_name, pn->pn_type); 171 } 172 /* 173 * Propagate flag through to vnode so users know it can change 174 * if the process changes (i.e. execve) 175 */ 176 if ((pn->pn_flags & PFS_PROCDEP) != 0) 177 (*vpp)->v_vflag |= VV_PROCDEP; 178 pvd->pvd_vnode = *vpp; 179 mtx_lock(&pfs_vncache_mutex); 180 pvd->pvd_prev = NULL; 181 pvd->pvd_next = pfs_vncache; 182 if (pvd->pvd_next) 183 pvd->pvd_next->pvd_prev = pvd; 184 pfs_vncache = pvd; 185 mtx_unlock(&pfs_vncache_mutex); 186 (*vpp)->v_vnlock->lk_flags |= LK_CANRECURSE; 187 vn_lock(*vpp, LK_RETRY | LK_EXCLUSIVE, curthread); 188 return (0); 189 } 190 191 /* 192 * Free a vnode 193 */ 194 int 195 pfs_vncache_free(struct vnode *vp) 196 { 197 struct pfs_vdata *pvd; 198 199 mtx_lock(&pfs_vncache_mutex); 200 pvd = (struct pfs_vdata *)vp->v_data; 201 KASSERT(pvd != NULL, ("pfs_vncache_free(): no vnode data\n")); 202 if (pvd->pvd_next) 203 pvd->pvd_next->pvd_prev = pvd->pvd_prev; 204 if (pvd->pvd_prev) 205 pvd->pvd_prev->pvd_next = pvd->pvd_next; 206 else 207 pfs_vncache = pvd->pvd_next; 208 mtx_unlock(&pfs_vncache_mutex); 209 210 --pfs_vncache_entries; 211 FREE(pvd, M_PFSVNCACHE); 212 vp->v_data = NULL; 213 return (0); 214 } 215 216 /* 217 * Free all vnodes associated with a defunct process 218 * 219 * XXXRW: It is unfortunate that pfs_exit() always acquires and releases two 220 * mutexes (one of which is Giant) for every process exit, even if procfs 221 * isn't mounted. 222 */ 223 static void 224 pfs_exit(void *arg, struct proc *p) 225 { 226 struct pfs_vdata *pvd; 227 struct vnode *vnp; 228 229 mtx_lock(&Giant); 230 /* 231 * This is extremely inefficient due to the fact that vgone() not 232 * only indirectly modifies the vnode cache, but may also sleep. 233 * We can neither hold pfs_vncache_mutex across a vgone() call, 234 * nor make any assumptions about the state of the cache after 235 * vgone() returns. In consequence, we must start over after 236 * every vgone() call, and keep trying until we manage to traverse 237 * the entire cache. 238 * 239 * The only way to improve this situation is to change the data 240 * structure used to implement the cache. An obvious choice in 241 * this particular case would be a BST sorted by PID. 242 */ 243 mtx_lock(&pfs_vncache_mutex); 244 pvd = pfs_vncache; 245 while (pvd != NULL) { 246 if (pvd->pvd_pid == p->p_pid) { 247 vnp = pvd->pvd_vnode; 248 mtx_unlock(&pfs_vncache_mutex); 249 vgone(vnp); 250 mtx_lock(&pfs_vncache_mutex); 251 pvd = pfs_vncache; 252 } else { 253 pvd = pvd->pvd_next; 254 } 255 } 256 mtx_unlock(&pfs_vncache_mutex); 257 mtx_unlock(&Giant); 258 } 259 260 /* 261 * Disable a pseudofs node, and free all vnodes associated with it 262 */ 263 int 264 pfs_disable(struct pfs_node *pn) 265 { 266 struct pfs_vdata *pvd; 267 struct vnode *vnp; 268 269 if (pn->pn_flags & PFS_DISABLED) 270 return (0); 271 pn->pn_flags |= PFS_DISABLED; 272 /* XXX see comment above nearly identical code in pfs_exit() */ 273 mtx_lock(&pfs_vncache_mutex); 274 pvd = pfs_vncache; 275 while (pvd != NULL) { 276 if (pvd->pvd_pn == pn) { 277 vnp = pvd->pvd_vnode; 278 mtx_unlock(&pfs_vncache_mutex); 279 vgone(vnp); 280 mtx_lock(&pfs_vncache_mutex); 281 pvd = pfs_vncache; 282 } else { 283 pvd = pvd->pvd_next; 284 } 285 } 286 mtx_unlock(&pfs_vncache_mutex); 287 return (0); 288 } 289 290 /* 291 * Re-enable a disabled pseudofs node 292 */ 293 int 294 pfs_enable(struct pfs_node *pn) 295 { 296 pn->pn_flags &= ~PFS_DISABLED; 297 return (0); 298 } 299