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