Change union to vcache. Use address of the union node as key.

It would be better to use (uppervp, lowervp) as key, but either
may be NULL and may change any time.
This commit is contained in:
hannken 2015-02-16 10:22:00 +00:00
parent c20c5717d4
commit 8596fa407f
3 changed files with 120 additions and 117 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: union.h,v 1.27 2015/02/16 10:21:25 hannken Exp $ */ /* $NetBSD: union.h,v 1.28 2015/02/16 10:22:00 hannken Exp $ */
/* /*
* Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994 The Regents of the University of California.
@ -121,6 +121,7 @@ struct union_node {
kmutex_t un_lock; kmutex_t un_lock;
LIST_ENTRY(union_node) un_cache; /* c: Hash chain */ LIST_ENTRY(union_node) un_cache; /* c: Hash chain */
int un_refs; /* c: Reference counter */ int un_refs; /* c: Reference counter */
struct mount *un_mount; /* c: union mount */
struct vnode *un_vnode; /* :: Back pointer */ struct vnode *un_vnode; /* :: Back pointer */
struct vnode *un_uppervp; /* m: overlaying object */ struct vnode *un_uppervp; /* m: overlaying object */
struct vnode *un_lowervp; /* v: underlying object */ struct vnode *un_lowervp; /* v: underlying object */
@ -162,6 +163,8 @@ extern void union_newupper(struct union_node *, struct vnode *);
extern void union_newsize(struct vnode *, off_t, off_t); extern void union_newsize(struct vnode *, off_t, off_t);
int union_readdirhook(struct vnode **, struct file *, struct lwp *); int union_readdirhook(struct vnode **, struct file *, struct lwp *);
VFS_PROTOS(union);
#define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data)) #define MOUNTTOUNIONMOUNT(mp) ((struct union_mount *)((mp)->mnt_data))
#define VTOUNION(vp) ((struct union_node *)(vp)->v_data) #define VTOUNION(vp) ((struct union_node *)(vp)->v_data)
#define UNIONTOV(un) ((un)->un_vnode) #define UNIONTOV(un) ((un)->un_vnode)

View File

@ -1,4 +1,4 @@
/* $NetBSD: union_subr.c,v 1.69 2015/02/16 10:21:25 hannken Exp $ */ /* $NetBSD: union_subr.c,v 1.70 2015/02/16 10:22:00 hannken Exp $ */
/* /*
* Copyright (c) 1994 * Copyright (c) 1994
@ -72,7 +72,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.69 2015/02/16 10:21:25 hannken Exp $"); __KERNEL_RCSID(0, "$NetBSD: union_subr.c,v 1.70 2015/02/16 10:22:00 hannken Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -344,22 +344,12 @@ union_rele(struct union_node *un)
* the reference is either maintained in the new union_node * the reference is either maintained in the new union_node
* object which is allocated, or they are vrele'd. * object which is allocated, or they are vrele'd.
* *
* all union_nodes are maintained on a singly-linked * all union_nodes are maintained on a hash
* list. new nodes are only allocated when they cannot * list. new nodes are only allocated when they cannot
* be found on this list. entries on the list are * be found on this list. entries on the list are
* removed when the vfs reclaim entry is called. * removed when the vfs reclaim entry is called.
* *
* a single lock is kept for the entire list. this is * the vnode gets attached or referenced with vcache_get().
* needed because the getnewvnode() function can block
* waiting for a vnode to become free, in which case there
* may be more than one process trying to get the same
* vnode. this lock is only taken if we are going to
* call getnewvnode, since the kernel itself is single-threaded.
*
* if an entry is found on the list, then call vget() to
* take a reference. this is done because there may be
* zero references to it and so it needs to removed from
* the vnode free list.
*/ */
int int
union_allocvp( union_allocvp(
@ -373,14 +363,9 @@ union_allocvp(
int docache) int docache)
{ {
int error; int error;
struct vattr va;
struct union_node *un = NULL, *un1; struct union_node *un = NULL, *un1;
struct vnode *vp, *xlowervp = NULLVP; struct vnode *vp, *xlowervp = NULLVP;
struct union_mount *um = MOUNTTOUNIONMOUNT(mp);
voff_t uppersz, lowersz;
dev_t rdev;
u_long hash[3]; u_long hash[3];
int vflag, iflag;
int try; int try;
bool is_dotdot; bool is_dotdot;
@ -394,20 +379,6 @@ union_allocvp(
lowervp = NULLVP; lowervp = NULLVP;
} }
/* detect the root vnode (and aliases) */
iflag = VI_LAYER;
vflag = 0;
if ((uppervp == um->um_uppervp) &&
((lowervp == NULLVP) || lowervp == um->um_lowervp)) {
if (lowervp == NULLVP) {
lowervp = um->um_lowervp;
if (lowervp != NULLVP)
vref(lowervp);
}
iflag = 0;
vflag = VV_ROOT;
}
if (!docache) { if (!docache) {
un = NULL; un = NULL;
goto found; goto found;
@ -434,17 +405,18 @@ loop:
LIST_FOREACH(un, &uhashtbl[hash[try]], un_cache) { LIST_FOREACH(un, &uhashtbl[hash[try]], un_cache) {
if ((un->un_lowervp && un->un_lowervp != lowervp) || if ((un->un_lowervp && un->un_lowervp != lowervp) ||
(un->un_uppervp && un->un_uppervp != uppervp) || (un->un_uppervp && un->un_uppervp != uppervp) ||
UNIONTOV(un)->v_mount != mp) un->un_mount != mp)
continue; continue;
vp = UNIONTOV(un);
union_ref(un); union_ref(un);
mutex_enter(vp->v_interlock);
mutex_exit(&uhash_lock); mutex_exit(&uhash_lock);
error = vget(vp, 0); error = vcache_get(mp, &un, sizeof(un), &vp);
KASSERT(error != 0 || UNIONTOV(un) == vp);
union_rele(un); union_rele(un);
if (error) if (error == ENOENT)
goto loop; goto loop;
else if (error)
goto out;
goto found; goto found;
} }
} }
@ -492,77 +464,15 @@ found:
*vpp = UNIONTOV(un); *vpp = UNIONTOV(un);
if (uppervp != dvp) if (uppervp != dvp)
VOP_UNLOCK(*vpp); VOP_UNLOCK(*vpp);
return (0); error = 0;
goto out;
} }
uppersz = lowersz = VNOVAL; un = malloc(sizeof(struct union_node), M_TEMP, M_WAITOK);
if (uppervp != NULLVP) {
vn_lock(uppervp, LK_SHARED | LK_RETRY);
if (VOP_GETATTR(uppervp, &va, FSCRED) == 0)
uppersz = va.va_size;
VOP_UNLOCK(uppervp);
}
if (lowervp != NULLVP) {
vn_lock(lowervp, LK_SHARED | LK_RETRY);
if (VOP_GETATTR(lowervp, &va, FSCRED) == 0)
lowersz = va.va_size;
VOP_UNLOCK(lowervp);
}
/*
* Get a new vnode and share the lock with upper layer vnode,
* unless layers are inverted.
*/
vnode_t *svp = (uppervp != NULLVP) ? uppervp : lowervp;
error = getnewvnode(VT_UNION, mp, union_vnodeop_p,
svp->v_interlock, vpp);
if (error) {
if (uppervp)
vrele(uppervp);
if (lowervp)
vrele(lowervp);
return error;
}
if (docache) {
mutex_enter(&uhash_lock);
LIST_FOREACH(un1, &uhashtbl[hash[0]], un_cache) {
if (un1->un_lowervp == lowervp &&
un1->un_uppervp == uppervp &&
UNIONTOV(un1)->v_mount == mp) {
/*
* Another thread beat us, push back freshly
* allocated vnode and retry.
*/
mutex_exit(&uhash_lock);
ungetnewvnode(*vpp);
goto loop;
}
}
}
(*vpp)->v_data = malloc(sizeof(struct union_node), M_TEMP, M_WAITOK);
(*vpp)->v_vflag |= vflag;
(*vpp)->v_iflag |= iflag;
rdev = NODEV;
if (uppervp) {
(*vpp)->v_type = uppervp->v_type;
if (uppervp->v_type == VCHR || uppervp->v_type == VBLK)
rdev = uppervp->v_rdev;
} else {
(*vpp)->v_type = lowervp->v_type;
if (lowervp->v_type == VCHR || lowervp->v_type == VBLK)
rdev = lowervp->v_rdev;
}
if (rdev != NODEV)
spec_node_init(*vpp, rdev);
un = VTOUNION(*vpp);
mutex_init(&un->un_lock, MUTEX_DEFAULT, IPL_NONE); mutex_init(&un->un_lock, MUTEX_DEFAULT, IPL_NONE);
un->un_refs = 1; un->un_refs = 1;
un->un_vnode = *vpp; un->un_mount = mp;
un->un_vnode = NULL;
un->un_uppervp = uppervp; un->un_uppervp = uppervp;
un->un_lowervp = lowervp; un->un_lowervp = lowervp;
un->un_pvp = undvp; un->un_pvp = undvp;
@ -572,10 +482,8 @@ found:
un->un_openl = 0; un->un_openl = 0;
un->un_cflags = 0; un->un_cflags = 0;
mutex_enter(&un->un_lock);
un->un_uppersz = VNOVAL; un->un_uppersz = VNOVAL;
un->un_lowersz = VNOVAL; un->un_lowersz = VNOVAL;
union_newsize(*vpp, uppersz, lowersz);
if (dvp && cnp && (lowervp != NULLVP)) { if (dvp && cnp && (lowervp != NULLVP)) {
un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK); un->un_path = malloc(cnp->cn_namelen+1, M_TEMP, M_WAITOK);
@ -589,17 +497,36 @@ found:
} }
if (docache) { if (docache) {
mutex_enter(&uhash_lock);
LIST_FOREACH(un1, &uhashtbl[hash[0]], un_cache) {
if (un1->un_lowervp == lowervp &&
un1->un_uppervp == uppervp &&
un1->un_mount == mp) {
/*
* Another thread beat us, push back freshly
* allocated node and retry.
*/
mutex_exit(&uhash_lock);
union_rele(un);
goto loop;
}
}
LIST_INSERT_HEAD(&uhashtbl[hash[0]], un, un_cache); LIST_INSERT_HEAD(&uhashtbl[hash[0]], un, un_cache);
un->un_cflags |= UN_CACHED; un->un_cflags |= UN_CACHED;
mutex_exit(&uhash_lock);
} }
error = vcache_get(mp, &un, sizeof(un), vpp);
KASSERT(error != 0 || UNIONTOV(un) == *vpp);
union_rele(un);
if (error == ENOENT)
goto loop;
out:
if (xlowervp) if (xlowervp)
vrele(xlowervp); vrele(xlowervp);
if (docache) return error;
mutex_exit(&uhash_lock);
return (error);
} }
int int
@ -607,9 +534,83 @@ union_freevp(struct vnode *vp)
{ {
struct union_node *un = VTOUNION(vp); struct union_node *un = VTOUNION(vp);
vcache_remove(vp->v_mount, &un, sizeof(un));
mutex_enter(vp->v_interlock);
vp->v_data = NULL;
mutex_exit(vp->v_interlock);
union_rele(un); union_rele(un);
vp->v_data = NULL; return 0;
}
int
union_loadvnode(struct mount *mp, struct vnode *vp,
const void *key, size_t key_len, const void **new_key)
{
struct vattr va;
struct vnode *svp;
struct union_node *un;
struct union_mount *um;
voff_t uppersz, lowersz;
KASSERT(key_len == sizeof(un));
memcpy(&un, key, key_len);
um = MOUNTTOUNIONMOUNT(mp);
svp = (un->un_uppervp != NULLVP) ? un->un_uppervp : un->un_lowervp;
vp->v_tag = VT_UNION;
vp->v_op = union_vnodeop_p;
vp->v_data = un;
un->un_vnode = vp;
vp->v_type = svp->v_type;
if (svp->v_type == VCHR || svp->v_type == VBLK)
spec_node_init(vp, svp->v_rdev);
mutex_obj_hold(svp->v_interlock);
uvm_obj_setlock(&vp->v_uobj, svp->v_interlock);
vp->v_iflag |= VI_LOCKSHARE;
/* detect the root vnode (and aliases) */
if ((un->un_uppervp == um->um_uppervp) &&
((un->un_lowervp == NULLVP) || un->un_lowervp == um->um_lowervp)) {
if (un->un_lowervp == NULLVP) {
un->un_lowervp = um->um_lowervp;
if (un->un_lowervp != NULLVP)
vref(un->un_lowervp);
}
vp->v_vflag |= VV_ROOT;
} else {
vp->v_iflag |= VI_LAYER;
}
uppersz = lowersz = VNOVAL;
if (un->un_uppervp != NULLVP) {
if (vn_lock(un->un_uppervp, LK_SHARED) == 0) {
if (VOP_GETATTR(un->un_uppervp, &va, FSCRED) == 0)
uppersz = va.va_size;
VOP_UNLOCK(un->un_uppervp);
}
}
if (un->un_lowervp != NULLVP) {
if (vn_lock(un->un_lowervp, LK_SHARED) == 0) {
if (VOP_GETATTR(un->un_lowervp, &va, FSCRED) == 0)
lowersz = va.va_size;
VOP_UNLOCK(un->un_lowervp);
}
}
mutex_enter(&un->un_lock);
union_newsize(vp, uppersz, lowersz);
mutex_enter(&uhash_lock);
union_ref(un);
mutex_exit(&uhash_lock);
*new_key = &vp->v_data;
return 0; return 0;
} }

View File

@ -1,4 +1,4 @@
/* $NetBSD: union_vfsops.c,v 1.73 2014/10/18 08:33:29 snj Exp $ */ /* $NetBSD: union_vfsops.c,v 1.74 2015/02/16 10:22:00 hannken Exp $ */
/* /*
* Copyright (c) 1994 The Regents of the University of California. * Copyright (c) 1994 The Regents of the University of California.
@ -77,7 +77,7 @@
*/ */
#include <sys/cdefs.h> #include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.73 2014/10/18 08:33:29 snj Exp $"); __KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.74 2015/02/16 10:22:00 hannken Exp $");
#include <sys/param.h> #include <sys/param.h>
#include <sys/systm.h> #include <sys/systm.h>
@ -98,8 +98,6 @@ __KERNEL_RCSID(0, "$NetBSD: union_vfsops.c,v 1.73 2014/10/18 08:33:29 snj Exp $"
MODULE(MODULE_CLASS_VFS, union, NULL); MODULE(MODULE_CLASS_VFS, union, NULL);
VFS_PROTOS(union);
static struct sysctllog *union_sysctl_log; static struct sysctllog *union_sysctl_log;
/* /*
@ -518,6 +516,7 @@ struct vfsops union_vfsops = {
.vfs_statvfs = union_statvfs, .vfs_statvfs = union_statvfs,
.vfs_sync = union_sync, .vfs_sync = union_sync,
.vfs_vget = union_vget, .vfs_vget = union_vget,
.vfs_loadvnode = union_loadvnode,
.vfs_fhtovp = (void *)eopnotsupp, .vfs_fhtovp = (void *)eopnotsupp,
.vfs_vptofh = (void *)eopnotsupp, .vfs_vptofh = (void *)eopnotsupp,
.vfs_init = union_init, .vfs_init = union_init,