NetBSD/sys/miscfs/lofs/lofs_subr.c

342 lines
8.0 KiB
C

/*
* Copyright (c) 1992 The Regents of the University of California
* Copyright (c) 1990, 1992 Jan-Simon Pendry
* All rights reserved.
*
* This code is derived from software donated to Berkeley by
* Jan-Simon Pendry.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* From:
* Id: lofs_subr.c,v 4.1 1993/12/17 10:47:45 jsp Rel
*
* $Id: lofs_subr.c,v 1.1 1994/01/05 14:15:34 cgd Exp $
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <sys/namei.h>
#include <miscfs/lofs/lofs.h>
#define LOG2_SIZEVNODE 8 /* log2(sizeof struct vnode) */
#define NLOFSCACHE 16
#define LOFS_NHASH(vp) ((((u_long)vp)>>LOG2_SIZEVNODE) & (NLOFSCACHE-1))
/*
* Loopback cache:
* Each cache entry holds a reference to the target vnode
* along with a pointer to the alias vnode. When an
* entry is added the target vnode is VREF'd. When the
* alias is removed the target vnode is vrele'd.
*/
/*
* Cache head
*/
struct lofscache {
struct lofsnode *ac_forw;
struct lofsnode *ac_back;
};
static struct lofscache lofscache[NLOFSCACHE];
/*
* Initialise cache headers
*/
lofs_init()
{
struct lofscache *ac;
#ifdef LOFS_DIAGNOSTIC
printf("lofs_init\n"); /* printed during system boot */
#endif
if (sizeof(struct lofsnode) > VN_MAXPRIVATE)
printf("lofsnode: ERROR: sizeof(struct lofsnode) > VN_MAXPRIVATE\n");
for (ac = lofscache; ac < lofscache + NLOFSCACHE; ac++)
ac->ac_forw = ac->ac_back = (struct lofsnode *) ac;
}
/*
* Compute hash list for given target vnode
*/
static struct lofscache *
lofs_hash(targetvp)
struct vnode *targetvp;
{
return (&lofscache[LOFS_NHASH(targetvp)]);
}
/*
* Make a new lofsnode node.
* Vp is the alias vnode, lofsvp is the target vnode.
* Maintain a reference to (targetvp).
*/
static void
lofs_alloc(vp, targetvp)
struct vnode *vp;
struct vnode *targetvp;
{
struct lofscache *hd;
struct lofsnode *a;
#ifdef LOFS_DIAGNOSTIC
printf("lofs_alloc(%x, %x)\n", vp, targetvp);
#endif
a = LOFSP(vp);
a->a_vnode = vp;
VREF(targetvp);
LOFSP(vp)->a_lofsvp = targetvp;
hd = lofs_hash(targetvp);
insque(a, hd);
#ifdef LOFS_DIAGNOSTIC
vprint("alloc vp", vp);
vprint("alloc targetvp", targetvp);
#endif
}
#ifdef LOFS_DIAGNOSTIC
void
lofs_flushmp(mp)
struct mount *mp;
{
struct lofscache *ac;
int i = 0;
struct lofsnode *roota;
printf("lofs_flushmp(%x)\n", mp);
roota = LOFSP(VFSTOLOFS(mp)->rootvp);
for (ac = lofscache; ac < lofscache + NLOFSCACHE; ac++) {
struct lofsnode *a = ac->ac_forw;
while (a != (struct lofsnode *) ac) {
if (a != roota && a->a_vnode->v_mount == mp) {
struct vnode *vp = a->a_lofsvp;
if (vp) {
a->a_lofsvp = 0;
vprint("would vrele", vp);
/*vrele(vp);*/
i++;
}
}
a = a->a_forw;
}
}
if (i > 0)
printf("lofsnode: vrele'd %d aliases\n", i);
}
#endif
/*
* Return alias for target vnode if already exists, else 0.
*/
static struct lofsnode *
lofs_find(mp, targetvp)
struct mount *mp;
struct vnode *targetvp;
{
struct lofscache *hd;
struct lofsnode *a;
#ifdef LOFS_DIAGNOSTIC
printf("lofs_find(mp = %x, target = %x)\n", mp, targetvp);
#endif
/*
* Find hash base, and then search the (two-way) linked
* list looking for a lofsnode structure which is referencing
* the target vnode. If found, the increment the lofsnode
* reference count (but NOT the target vnode's VREF counter).
*/
hd = lofs_hash(targetvp);
for (a = hd->ac_forw; a != (struct lofsnode *) hd; a = a->a_forw) {
if (a->a_lofsvp == targetvp && a->a_vnode->v_mount == mp) {
#ifdef LOFS_DIAGNOSTIC
printf("lofs_find(%x): found (%x,%x)->%x\n",
targetvp, mp, a->a_vnode, targetvp);
#endif
return (a);
}
}
#ifdef LOFS_DIAGNOSTIC
printf("lofs_find(%x, %x): NOT found\n", mp, targetvp);
#endif
return (0);
}
static int
lofs_alias(mp, targetvp, newvpp)
struct mount *mp;
struct vnode *targetvp;
struct vnode **newvpp;
{
struct lofsnode *ap;
struct vnode *aliasvp;
if (targetvp->v_type != VDIR || targetvp->v_op == &lofs_vnodeops) {
*newvpp = targetvp;
return;
}
ap = lofs_find(mp, targetvp);
if (ap) {
/*
* Take another reference to the alias vnode
*/
#ifdef LOFS_DIAGNOSTIC
vprint("lofs_alias: exists", ap->a_vnode);
#endif
aliasvp = ap->a_vnode;
VREF(aliasvp);
} else {
int error;
/*
* Get new vnode.
*/
#ifdef LOFS_DIAGNOSTIC
printf("lofs_alias: create new alias vnode\n");
#endif
if (error = getnewvnode(VT_UFS, mp, &lofs_vnodeops, &aliasvp))
return (error); /* XXX: VT_LOFS above */
/*
* Must be a directory
*/
aliasvp->v_type = VDIR;
/*
* Make new vnode reference the lofsnode.
*/
lofs_alloc(aliasvp, targetvp);
/*
* aliasvp is already VREF'd by getnewvnode()
*/
}
vrele(targetvp);
#ifdef LOFS_DIAGNOSTIC
vprint("lofs_alias alias", aliasvp);
vprint("lofs_alias target", targetvp);
#endif
*newvpp = aliasvp;
return (0);
}
/*
* Try to find an existing lofsnode vnode refering
* to it, otherwise make a new lofsnode vnode which
* contains a reference to the target vnode.
*/
make_lofs(mp, ndp)
struct mount *mp;
struct nameidata *ndp;
{
int error;
struct vnode *aliasvp;
struct vnode *aliasdvp;
struct vnode *targetvp;
struct vnode *targetdvp;
#ifdef LOFS_DIAGNOSTIC
printf("make_lofs(mp = %x, nidvp = %x, nivp = %x)\n",
mp, ndp->ni_dvp, ndp->ni_vp);
#endif
/*
* (targetvp) is locked at this point.
*/
targetvp = ndp->ni_vp;
targetdvp = ndp->ni_dvp;
#ifdef LOFS_DIAGNOSTIC
if (targetvp == 0)
panic("make_lofs: null vp");
#endif
/*
* Try to find an existing reference to the target vnodes.
*/
error = lofs_alias(mp, targetvp, &aliasvp);
if (error)
return (error);
if (targetdvp) {
error = lofs_alias(mp, targetdvp, &aliasdvp);
if (error)
return (error);
} else
aliasdvp = NULLVP;
ndp->ni_vp = aliasvp;
ndp->ni_dvp = aliasdvp;
return (0);
}
#ifdef LOFS_DIAGNOSTIC
struct vnode *
lofs_checkvp(vp, fil, lno)
struct vnode *vp;
char *fil;
int lno;
{
struct lofsnode *a = LOFSP(vp);
if (a->a_lofsvp == 0) {
int i; u_long *p;
printf("vp = %x, ZERO ptr\n", vp);
#ifdef notdef
for (p = (u_long *) a, i = 0; i < 8; i++)
printf(" %x", p[i]);
printf("\n");
DELAY(2000000);
panic("lofs_checkvp");
#endif
}
printf("aliasvp %x/%d -> %x/%d [%s, %d]\n",
a->a_vnode, a->a_vnode->v_usecount,
a->a_lofsvp, a->a_lofsvp ? a->a_lofsvp->v_usecount : -42,
fil, lno);
return a->a_lofsvp;
}
#endif