Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 18 Sep 2007 14:35:02 +0900
From:      Daichi GOTO <daichi@ongs.co.jp>
To:        Kurt Jaeger <pi@c0mplx.org>,  freebsd-stable@freebsd.org,  FreeBSD Current <freebsd-current@freebsd.org>
Subject:   Re: Unionfs patchset p19 commit?
Message-ID:  <46EF6386.9050700@ongs.co.jp>
In-Reply-To: <20070915170104.GF2061@home.c0mplx.org>
References:  <200709141103.18612.h.schmalzbauer@omnisec.de> <20070914110024.G14481@fledge.watson.org> <46EBEA18.3080800@ongs.co.jp> <20070915170104.GF2061@home.c0mplx.org>

next in thread | previous in thread | raw e-mail | index | archive | help
This is a multi-part message in MIME format.
--------------070007080303020601050807
Content-Type: text/plain; charset=ISO-8859-1; format=flowed
Content-Transfer-Encoding: 7bit

Kurt Jaeger wrote:
> Hi!
> 
>> I have a big hope to get merged into FreeBSD until
>> 7-RELEASE. Progress is step by step slowly, but going
>> forward absolutely.  If you have interest in unionfs
>> improvements, push your passion to re@ and fs@ committers ;-)
> 
> Did you have a chance to look into this ?
> 
> http://lists.freebsd.org/pipermail/freebsd-stable/2007-June/035798.html
> 
> Thanks!

Already we have fixed above issue. But it is not in a first merge
patches bunch. FAI, I include a patch that has should been patched
after p19. Try it. (unionfs6 for stable, unionfs for current).

First, we should merge current unionfs patch into current tree.
Above issue patch will be merged after that work. Well, whatsoever,
we should do first merge work done :)

-- 
   Daichi GOTO, http://people.freebsd.org/~daichi

--------------070007080303020601050807
Content-Type: text/x-patch;
 name="unionfs6-20070712.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="unionfs6-20070712.diff"

diff -urBN /usr/src.p19/sys/fs/unionfs/union.h /usr/src/sys/fs/unionfs/union.h
--- /usr/src.p19/sys/fs/unionfs/union.h	Thu Jul 12 21:01:05 2007
+++ /usr/src/sys/fs/unionfs/union.h	Thu Jul 12 22:20:51 2007
@@ -84,7 +84,12 @@
 	struct vnode   *un_uppervp;		/* upper side vnode */
 	struct vnode   *un_dvp;			/* parent unionfs vnode */
 	struct vnode   *un_vnode;		/* Back pointer */
-	LIST_HEAD(, unionfs_node_status) un_unshead;  /* unionfs status head */
+	LIST_HEAD(, unionfs_node_status) un_unshead;
+						/* unionfs status head */
+	LIST_HEAD(unionfs_node_hashhead, unionfs_node) *un_hashtbl;
+						/* dir vnode hash table */
+	LIST_ENTRY(unionfs_node)   un_hash;	/* hash list entry */
+	u_long		un_hashmask;		/* bit mask */
 	char           *un_path;		/* path */
 	int		un_flag;		/* unionfs node flag */
 };
diff -urBN /usr/src.p19/sys/fs/unionfs/union_subr.c /usr/src/sys/fs/unionfs/union_subr.c
--- /usr/src.p19/sys/fs/unionfs/union_subr.c	Thu Jul 12 21:01:05 2007
+++ /usr/src/sys/fs/unionfs/union_subr.c	Thu Jul 12 22:20:51 2007
@@ -60,6 +60,9 @@
 
 #include <fs/unionfs/union.h>
 
+#define NUNIONFSNODECACHE 16
+
+static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table");
 MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
 MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
 
@@ -82,6 +85,117 @@
 	return (0);
 }
 
+static struct unionfs_node_hashhead *
+unionfs_get_hashhead(struct vnode *dvp, char *path)
+{
+	int		count;
+	char		hash;
+	struct unionfs_node *unp;
+
+	hash = 0;
+	unp = VTOUNIONFS(dvp);
+	if (path != NULL) {
+		for (count = 0; path[count]; count++)
+			hash += path[count];
+	}
+
+	return (&(unp->un_hashtbl[hash & (unp->un_hashmask)]));
+}
+
+/*
+ * Get the cached vnode. (only VDIR)
+ */
+static struct vnode *
+unionfs_get_cached_vdir(struct vnode *uvp, struct vnode *lvp,
+			struct vnode *dvp, char *path)
+{
+	struct unionfs_node_hashhead *hd;
+	struct unionfs_node *unp;
+	struct vnode   *vp;
+
+	KASSERT((uvp == NULLVP || uvp->v_type == VDIR),
+	        ("unionfs_get_cached_vdir: v_type != VDIR"));
+	KASSERT((lvp == NULLVP || lvp->v_type == VDIR),
+	        ("unionfs_get_cached_vdir: v_type != VDIR"));
+
+	VI_LOCK(dvp);
+	hd = unionfs_get_hashhead(dvp, path);
+	LIST_FOREACH(unp, hd, un_hash) {
+		if (!strcmp(unp->un_path, path)) {
+			vp = UNIONFSTOV(unp);
+			VI_LOCK_FLAGS(vp, MTX_DUPOK);
+			VI_UNLOCK(dvp);
+			vp->v_iflag &= ~VI_OWEINACT;
+			if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) {
+				VI_UNLOCK(vp);
+				vp = NULLVP;
+			} else
+				VI_UNLOCK(vp);
+			return (vp);
+		}
+	}
+	VI_UNLOCK(dvp);
+
+	return (NULLVP);
+}
+
+/*
+ * Add the new vnode into cache. (only VDIR)
+ */
+static struct vnode *
+unionfs_ins_cached_vdir(struct unionfs_node *uncp,
+			struct vnode *dvp, char *path)
+{
+	struct unionfs_node_hashhead *hd;
+	struct unionfs_node *unp;
+	struct vnode   *vp;
+
+	KASSERT((uncp->un_uppervp==NULLVP || uncp->un_uppervp->v_type==VDIR),
+	        ("unionfs_ins_cached_vdir: v_type != VDIR"));
+	KASSERT((uncp->un_lowervp==NULLVP || uncp->un_lowervp->v_type==VDIR),
+	        ("unionfs_ins_cached_vdir: v_type != VDIR"));
+
+	VI_LOCK(dvp);
+	hd = unionfs_get_hashhead(dvp, path);
+	LIST_FOREACH(unp, hd, un_hash) {
+		if (!strcmp(unp->un_path, path)) {
+			vp = UNIONFSTOV(unp);
+			VI_LOCK_FLAGS(vp, MTX_DUPOK);
+			vp->v_iflag &= ~VI_OWEINACT;
+			if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) {
+				LIST_INSERT_HEAD(hd, uncp, un_hash);
+				VI_UNLOCK(vp);
+				vp = NULLVP;
+			} else
+				VI_UNLOCK(vp);
+			VI_UNLOCK(dvp);
+			return (vp);
+		}
+	}
+
+	LIST_INSERT_HEAD(hd, uncp, un_hash);
+	VI_UNLOCK(dvp);
+
+	return (NULLVP);
+}
+
+/*
+ * Remove the vnode. (only VDIR)
+ */
+static void
+unionfs_rem_cached_vdir(struct unionfs_node *unp, struct vnode *dvp)
+{
+	KASSERT((unp != NULL), ("unionfs_rem_cached_vdir: null node"));
+	KASSERT((dvp != NULLVP),
+	    ("unionfs_rem_cached_vdir: null parent vnode"));
+	KASSERT((unp->un_hash.le_prev != NULL),
+	    ("unionfs_rem_cached_vdir: null hash"));
+
+	VI_LOCK(dvp);
+	LIST_REMOVE(unp, un_hash);
+	VI_UNLOCK(dvp);
+}
+
 /*
  * Make a new or get existing unionfs node.
  * 
@@ -100,21 +214,36 @@
 	struct vnode   *vp;
 	int		error;
 	int		lkflags;
+	enum vtype	vt;
 	char	       *path;
 
 	ump = MOUNTTOUNIONFSMOUNT(mp);
 	lkflags = (cnp ? cnp->cn_lkflags : 0);
 	path = (cnp ? cnp->cn_nameptr : NULL);
+	*vpp = NULLVP;
 
 	if (uppervp == NULLVP && lowervp == NULLVP)
 		panic("unionfs_nodeget: upper and lower is null");
 
+	vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
+
 	/* If it has no ISLASTCN flag, path check is skipped. */
 	if (cnp && !(cnp->cn_flags & ISLASTCN))
 		path = NULL;
 
+	/* check the vdir cache */
+	if (path != NULL && dvp != NULLVP && vt == VDIR) {
+		vp = unionfs_get_cached_vdir(uppervp, lowervp, dvp, path);
+		if (vp != NULLVP) {
+			vref(vp);
+			*vpp = vp;
+			goto unionfs_nodeget_out;
+		}
+	}
+
 	if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
 	    (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
+		/* dvp will be NULLVP only in case of root vnode. */
 		if (dvp == NULLVP)
 			return (EINVAL);
 	}
@@ -134,10 +263,18 @@
 	}
 	if (dvp != NULLVP)
 		vref(dvp);
-	if (uppervp != NULLVP)
+	if (uppervp != NULLVP) {
 		vref(uppervp);
-	if (lowervp != NULLVP)
+		vkernref(uppervp);
+	}
+	if (lowervp != NULLVP) {
 		vref(lowervp);
+		vkernref(lowervp);
+	}
+
+	if (vt == VDIR)
+		unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH,
+		    &(unp->un_hashmask));
 
 	unp->un_vnode = vp;
 	unp->un_uppervp = uppervp;
@@ -148,24 +285,46 @@
 	else
 		vp->v_vnlock = lowervp->v_vnlock;
 
-	if (path) {
+	if (path != NULL) {
 		unp->un_path = (char *)
 		    malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO);
 		bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
 		unp->un_path[cnp->cn_namelen] = '\0';
 	}
-	vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
+	vp->v_type = vt;
 	vp->v_data = unp;
 
 	if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
 	    (lowervp != NULLVP && ump->um_lowervp == lowervp))
 		vp->v_vflag |= VV_ROOT;
 
+	if (path != NULL && dvp != NULLVP && vt == VDIR)
+		*vpp = unionfs_ins_cached_vdir(unp, dvp, path);
+	if ((*vpp) != NULLVP) {
+		if (dvp != NULLVP)
+			vrele(dvp);
+		if (uppervp != NULLVP) {
+			vkernrele(uppervp);
+			vrele(uppervp);
+		}
+		if (lowervp != NULLVP) {
+			vkernrele(lowervp);
+			vrele(lowervp);
+		}
+
+		unp->un_uppervp = NULLVP;
+		unp->un_lowervp = NULLVP;
+		unp->un_dvp = NULLVP;
+		vrele(vp);
+		vp = *vpp;
+		vref(vp);
+	} else
+		*vpp = vp;
+
+unionfs_nodeget_out:
 	if (lkflags & LK_TYPE_MASK)
 		vn_lock(vp, lkflags | LK_RETRY, td);
 
-	*vpp = vp;
-
 	return (0);
 }
 
@@ -180,6 +339,7 @@
 	struct unionfs_node_status *unsp, *unsp_tmp;
 	struct vnode   *lvp;
 	struct vnode   *uvp;
+	struct vnode   *dvp;
 
 	/*
 	 * Use the interlock to protect the clearing of v_data to
@@ -189,6 +349,7 @@
 	unp = VTOUNIONFS(vp);
 	lvp = unp->un_lowervp;
 	uvp = unp->un_uppervp;
+	dvp = unp->un_dvp;
 	unp->un_lowervp = unp->un_uppervp = NULLVP;
 
 	vp->v_vnlock = &(vp->v_lock);
@@ -200,27 +361,35 @@
 		VOP_UNLOCK(uvp, 0, td);
 	vp->v_object = NULL;
 
+	if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR)
+		unionfs_rem_cached_vdir(unp, dvp);
+
 	if (lvp != NULLVP) {
 		vfslocked = VFS_LOCK_GIANT(lvp->v_mount);
+		vkernrele(lvp);
 		vrele(lvp);
 		VFS_UNLOCK_GIANT(vfslocked);
 	}
 	if (uvp != NULLVP) {
 		vfslocked = VFS_LOCK_GIANT(uvp->v_mount);
+		vkernrele(uvp);
 		vrele(uvp);
 		VFS_UNLOCK_GIANT(vfslocked);
 	}
-	if (unp->un_dvp != NULLVP) {
-		vfslocked = VFS_LOCK_GIANT(unp->un_dvp->v_mount);
-		vrele(unp->un_dvp);
+	if (dvp != NULLVP) {
+		vfslocked = VFS_LOCK_GIANT(dvp->v_mount);
+		vrele(dvp);
 		VFS_UNLOCK_GIANT(vfslocked);
 		unp->un_dvp = NULLVP;
 	}
-	if (unp->un_path) {
+	if (unp->un_path != NULL) {
 		free(unp->un_path, M_UNIONFSPATH);
 		unp->un_path = NULL;
 	}
 
+	if (unp->un_hashtbl != NULL)
+		hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, unp->un_hashmask);
+
 	LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) {
 		LIST_REMOVE(unsp, uns_list);
 		free(unsp, M_TEMP);
@@ -536,13 +705,16 @@
 	int		count, lockcnt;
 	struct vnode   *vp;
 	struct vnode   *lvp;
+	struct vnode   *dvp;
 
 	vp = UNIONFSTOV(unp);
 	lvp = unp->un_lowervp;
+	dvp = unp->un_dvp;
 
 	/*
 	 * lock update
 	 */
+	vkernref(uvp);
 	VI_LOCK(vp);
 	unp->un_uppervp = uvp;
 	vp->v_vnlock = uvp->v_vnlock;
@@ -552,6 +724,19 @@
 	VI_UNLOCK(vp);
 	for (count = 1; count < lockcnt; count++)
 		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, td);
+
+	/*
+	 * cache update
+	 */
+	if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR) {
+		static struct unionfs_node_hashhead *hd;
+
+		VI_LOCK(dvp);
+		hd = unionfs_get_hashhead(dvp, unp->un_path);
+		LIST_REMOVE(unp, un_hash);
+		LIST_INSERT_HEAD(hd, unp, un_hash);
+		VI_UNLOCK(dvp);
+	}
 }
 
 /*
diff -urBN /usr/src.p19/sys/kern/vfs_subr.c /usr/src/sys/kern/vfs_subr.c
--- /usr/src.p19/sys/kern/vfs_subr.c	Thu Jul 12 20:17:25 2007
+++ /usr/src/sys/kern/vfs_subr.c	Thu Jul 12 22:20:51 2007
@@ -944,6 +944,7 @@
 	vp->v_tag = tag;
 	vp->v_op = vops;
 	v_incr_usecount(vp);
+	vp->v_kernusecount = 0;
 	vp->v_data = 0;
 #ifdef MAC
 	mac_init_vnode(vp);
@@ -2063,6 +2064,8 @@
 	VNASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1, vp,
 	    ("vrele: missed vn_close"));
 
+	if (vp->v_usecount <= vp->v_kernusecount)
+		panic("vrele: kernel is referring to it");
 	if (vp->v_usecount > 1 || ((vp->v_iflag & VI_DOINGINACT) &&
 	    vp->v_usecount == 1)) {
 		v_decr_usecount(vp);
@@ -2102,6 +2105,32 @@
 }
 
 /*
+ * Increase the reference count of a vnode used by kernel.
+ */
+void
+vkernref(struct vnode *vp)
+{
+	VI_LOCK(vp);
+	if (vp->v_usecount <= vp->v_kernusecount)
+		panic("vkernref: vkernref call without vref");
+	vp->v_kernusecount++;
+	VI_UNLOCK(vp);
+}
+
+/*
+ * Decrease the reference count of a vnode used by kernel.
+ */
+void
+vkernrele(struct vnode *vp)
+{
+	VI_LOCK(vp);
+	if (vp->v_kernusecount < 1)
+		panic("vkernrele: negative kern ref count");
+	vp->v_kernusecount--;
+	VI_UNLOCK(vp);
+}
+
+/*
  * Release an already locked vnode.  This give the same effects as
  * unlock+vrele(), but takes less time and avoids releasing and
  * re-aquiring the lock (as vrele() aquires the lock internally.)
@@ -2336,7 +2365,8 @@
 		 *
 		 * If FORCECLOSE is set, forcibly close the vnode.
 		 */
-		if (vp->v_usecount == 0 || (flags & FORCECLOSE)) {
+		if ((vp->v_usecount == 0 || (flags & FORCECLOSE)) &&
+		    vp->v_kernusecount == 0) {
 			VNASSERT(vp->v_usecount == 0 ||
 			    (vp->v_type != VCHR && vp->v_type != VBLK), vp,
 			    ("device VNODE %p is FORCECLOSED", vp));
diff -urBN /usr/src.p19/sys/sys/vnode.h /usr/src/sys/sys/vnode.h
--- /usr/src.p19/sys/sys/vnode.h	Thu Jul 12 20:17:31 2007
+++ /usr/src/sys/sys/vnode.h	Thu Jul 12 22:20:51 2007
@@ -162,6 +162,7 @@
 	struct	lock *v_vnlock;			/* u pointer to vnode lock */
 	int	v_holdcnt;			/* i prevents recycling. */
 	int	v_usecount;			/* i ref count of users */
+	int	v_kernusecount;			/* i ref count of kernel */
 	u_long	v_iflag;			/* i vnode flags (see below) */
 	u_long	v_vflag;			/* v vnode flags */
 	int	v_writecount;			/* v ref count of writers */
@@ -704,6 +705,8 @@
 		    | (noffset > osize ? NOTE_EXTEND : 0));		\
 	}
 
+void	vkernrele(struct vnode *vp);
+void	vkernref(struct vnode *vp);
 void	vput(struct vnode *vp);
 void	vrele(struct vnode *vp);
 void	vref(struct vnode *vp);

--------------070007080303020601050807
Content-Type: text/x-patch;
 name="unionfs-20070712.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="unionfs-20070712.diff"

diff -urBN /usr/src.p19/sys/fs/unionfs/union.h /usr/src/sys/fs/unionfs/union.h
--- /usr/src.p19/sys/fs/unionfs/union.h	2007-07-10 01:38:29.000000000 +0900
+++ /usr/src/sys/fs/unionfs/union.h	2007-07-12 19:48:06.000000000 +0900
@@ -83,7 +83,12 @@
 	struct vnode   *un_uppervp;		/* upper side vnode */
 	struct vnode   *un_dvp;			/* parent unionfs vnode */
 	struct vnode   *un_vnode;		/* Back pointer */
-	LIST_HEAD(, unionfs_node_status) un_unshead;  /* unionfs status head */
+	LIST_HEAD(, unionfs_node_status) un_unshead;
+						/* unionfs status head */
+	LIST_HEAD(unionfs_node_hashhead, unionfs_node) *un_hashtbl;
+						/* dir vnode hash table */
+	LIST_ENTRY(unionfs_node)   un_hash;	/* hash list entry */
+	u_long		un_hashmask;		/* bit mask */
 	char           *un_path;		/* path */
 	int		un_flag;		/* unionfs node flag */
 };
diff -urBN /usr/src.p19/sys/fs/unionfs/union_subr.c /usr/src/sys/fs/unionfs/union_subr.c
--- /usr/src.p19/sys/fs/unionfs/union_subr.c	2007-07-10 01:38:16.000000000 +0900
+++ /usr/src/sys/fs/unionfs/union_subr.c	2007-07-12 21:57:09.000000000 +0900
@@ -60,6 +60,9 @@
 
 #include <fs/unionfs/union.h>
 
+#define NUNIONFSNODECACHE 16
+
+static MALLOC_DEFINE(M_UNIONFSHASH, "UNIONFS hash", "UNIONFS hash table");
 MALLOC_DEFINE(M_UNIONFSNODE, "UNIONFS node", "UNIONFS vnode private part");
 MALLOC_DEFINE(M_UNIONFSPATH, "UNIONFS path", "UNIONFS path private part");
 
@@ -82,6 +85,117 @@
 	return (0);
 }
 
+static struct unionfs_node_hashhead *
+unionfs_get_hashhead(struct vnode *dvp, char *path)
+{
+	int		count;
+	char		hash;
+	struct unionfs_node *unp;
+
+	hash = 0;
+	unp = VTOUNIONFS(dvp);
+	if (path != NULL) {
+		for (count = 0; path[count]; count++)
+			hash += path[count];
+	}
+
+	return (&(unp->un_hashtbl[hash & (unp->un_hashmask)]));
+}
+
+/*
+ * Get the cached vnode. (only VDIR)
+ */
+static struct vnode *
+unionfs_get_cached_vdir(struct vnode *uvp, struct vnode *lvp,
+			struct vnode *dvp, char *path)
+{
+	struct unionfs_node_hashhead *hd;
+	struct unionfs_node *unp;
+	struct vnode   *vp;
+
+	KASSERT((uvp == NULLVP || uvp->v_type == VDIR),
+	        ("unionfs_get_cached_vdir: v_type != VDIR"));
+	KASSERT((lvp == NULLVP || lvp->v_type == VDIR),
+	        ("unionfs_get_cached_vdir: v_type != VDIR"));
+
+	VI_LOCK(dvp);
+	hd = unionfs_get_hashhead(dvp, path);
+	LIST_FOREACH(unp, hd, un_hash) {
+		if (!strcmp(unp->un_path, path)) {
+			vp = UNIONFSTOV(unp);
+			VI_LOCK_FLAGS(vp, MTX_DUPOK);
+			VI_UNLOCK(dvp);
+			vp->v_iflag &= ~VI_OWEINACT;
+			if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) {
+				VI_UNLOCK(vp);
+				vp = NULLVP;
+			} else
+				VI_UNLOCK(vp);
+			return (vp);
+		}
+	}
+	VI_UNLOCK(dvp);
+
+	return (NULLVP);
+}
+
+/*
+ * Add the new vnode into cache. (only VDIR)
+ */
+static struct vnode *
+unionfs_ins_cached_vdir(struct unionfs_node *uncp,
+			struct vnode *dvp, char *path)
+{
+	struct unionfs_node_hashhead *hd;
+	struct unionfs_node *unp;
+	struct vnode   *vp;
+
+	KASSERT((uncp->un_uppervp==NULLVP || uncp->un_uppervp->v_type==VDIR),
+	        ("unionfs_ins_cached_vdir: v_type != VDIR"));
+	KASSERT((uncp->un_lowervp==NULLVP || uncp->un_lowervp->v_type==VDIR),
+	        ("unionfs_ins_cached_vdir: v_type != VDIR"));
+
+	VI_LOCK(dvp);
+	hd = unionfs_get_hashhead(dvp, path);
+	LIST_FOREACH(unp, hd, un_hash) {
+		if (!strcmp(unp->un_path, path)) {
+			vp = UNIONFSTOV(unp);
+			VI_LOCK_FLAGS(vp, MTX_DUPOK);
+			vp->v_iflag &= ~VI_OWEINACT;
+			if ((vp->v_iflag & (VI_DOOMED | VI_DOINGINACT)) != 0) {
+				LIST_INSERT_HEAD(hd, uncp, un_hash);
+				VI_UNLOCK(vp);
+				vp = NULLVP;
+			} else
+				VI_UNLOCK(vp);
+			VI_UNLOCK(dvp);
+			return (vp);
+		}
+	}
+
+	LIST_INSERT_HEAD(hd, uncp, un_hash);
+	VI_UNLOCK(dvp);
+
+	return (NULLVP);
+}
+
+/*
+ * Remove the vnode. (only VDIR)
+ */
+static void
+unionfs_rem_cached_vdir(struct unionfs_node *unp, struct vnode *dvp)
+{
+	KASSERT((unp != NULL), ("unionfs_rem_cached_vdir: null node"));
+	KASSERT((dvp != NULLVP),
+	    ("unionfs_rem_cached_vdir: null parent vnode"));
+	KASSERT((unp->un_hash.le_prev != NULL),
+	    ("unionfs_rem_cached_vdir: null hash"));
+
+	VI_LOCK(dvp);
+	LIST_REMOVE(unp, un_hash);
+	VI_UNLOCK(dvp);
+}
+
 /*
  * Make a new or get existing unionfs node.
  * 
@@ -100,21 +214,36 @@
 	struct vnode   *vp;
 	int		error;
 	int		lkflags;
+	enum vtype	vt;
 	char	       *path;
 
 	ump = MOUNTTOUNIONFSMOUNT(mp);
 	lkflags = (cnp ? cnp->cn_lkflags : 0);
 	path = (cnp ? cnp->cn_nameptr : NULL);
+	*vpp = NULLVP;
 
 	if (uppervp == NULLVP && lowervp == NULLVP)
 		panic("unionfs_nodeget: upper and lower is null");
 
+	vt = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
+
 	/* If it has no ISLASTCN flag, path check is skipped. */
 	if (cnp && !(cnp->cn_flags & ISLASTCN))
 		path = NULL;
 
+	/* check the vdir cache */
+	if (path != NULL && dvp != NULLVP && vt == VDIR) {
+		vp = unionfs_get_cached_vdir(uppervp, lowervp, dvp, path);
+		if (vp != NULLVP) {
+			vref(vp);
+			*vpp = vp;
+			goto unionfs_nodeget_out;
+		}
+	}
+
 	if ((uppervp == NULLVP || ump->um_uppervp != uppervp) ||
 	    (lowervp == NULLVP || ump->um_lowervp != lowervp)) {
+		/* dvp will be NULLVP only in case of root vnode. */
 		if (dvp == NULLVP)
 			return (EINVAL);
 	}
@@ -139,10 +268,18 @@
 	}
 	if (dvp != NULLVP)
 		vref(dvp);
-	if (uppervp != NULLVP)
+	if (uppervp != NULLVP) {
 		vref(uppervp);
-	if (lowervp != NULLVP)
+		vkernref(uppervp);
+	}
+	if (lowervp != NULLVP) {
 		vref(lowervp);
+		vkernref(lowervp);
+	}
+
+	if (vt == VDIR)
+		unp->un_hashtbl = hashinit(NUNIONFSNODECACHE, M_UNIONFSHASH,
+		    &(unp->un_hashmask));
 
 	unp->un_vnode = vp;
 	unp->un_uppervp = uppervp;
@@ -153,24 +290,46 @@
 	else
 		vp->v_vnlock = lowervp->v_vnlock;
 
-	if (path) {
+	if (path != NULL) {
 		unp->un_path = (char *)
 		    malloc(cnp->cn_namelen +1, M_UNIONFSPATH, M_WAITOK|M_ZERO);
 		bcopy(cnp->cn_nameptr, unp->un_path, cnp->cn_namelen);
 		unp->un_path[cnp->cn_namelen] = '\0';
 	}
-	vp->v_type = (uppervp != NULLVP ? uppervp->v_type : lowervp->v_type);
+	vp->v_type = vt;
 	vp->v_data = unp;
 
 	if ((uppervp != NULLVP && ump->um_uppervp == uppervp) &&
 	    (lowervp != NULLVP && ump->um_lowervp == lowervp))
 		vp->v_vflag |= VV_ROOT;
 
+	if (path != NULL && dvp != NULLVP && vt == VDIR)
+		*vpp = unionfs_ins_cached_vdir(unp, dvp, path);
+	if ((*vpp) != NULLVP) {
+		if (dvp != NULLVP)
+			vrele(dvp);
+		if (uppervp != NULLVP) {
+			vkernrele(uppervp);
+			vrele(uppervp);
+		}
+		if (lowervp != NULLVP) {
+			vkernrele(lowervp);
+			vrele(lowervp);
+		}
+
+		unp->un_uppervp = NULLVP;
+		unp->un_lowervp = NULLVP;
+		unp->un_dvp = NULLVP;
+		vrele(vp);
+		vp = *vpp;
+		vref(vp);
+	} else
+		*vpp = vp;
+
+unionfs_nodeget_out:
 	if (lkflags & LK_TYPE_MASK)
 		vn_lock(vp, lkflags | LK_RETRY, td);
 
-	*vpp = vp;
-
 	return (0);
 }
 
@@ -185,6 +344,7 @@
 	struct unionfs_node_status *unsp, *unsp_tmp;
 	struct vnode   *lvp;
 	struct vnode   *uvp;
+	struct vnode   *dvp;
 
 	/*
 	 * Use the interlock to protect the clearing of v_data to
@@ -194,6 +354,7 @@
 	unp = VTOUNIONFS(vp);
 	lvp = unp->un_lowervp;
 	uvp = unp->un_uppervp;
+	dvp = unp->un_dvp;
 	unp->un_lowervp = unp->un_uppervp = NULLVP;
 
 	vp->v_vnlock = &(vp->v_lock);
@@ -205,27 +366,35 @@
 		VOP_UNLOCK(uvp, 0, td);
 	vp->v_object = NULL;
 
+	if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR)
+		unionfs_rem_cached_vdir(unp, dvp);
+
 	if (lvp != NULLVP) {
 		vfslocked = VFS_LOCK_GIANT(lvp->v_mount);
+		vkernrele(lvp);
 		vrele(lvp);
 		VFS_UNLOCK_GIANT(vfslocked);
 	}
 	if (uvp != NULLVP) {
 		vfslocked = VFS_LOCK_GIANT(uvp->v_mount);
+		vkernrele(uvp);
 		vrele(uvp);
 		VFS_UNLOCK_GIANT(vfslocked);
 	}
-	if (unp->un_dvp != NULLVP) {
-		vfslocked = VFS_LOCK_GIANT(unp->un_dvp->v_mount);
-		vrele(unp->un_dvp);
+	if (dvp != NULLVP) {
+		vfslocked = VFS_LOCK_GIANT(dvp->v_mount);
+		vrele(dvp);
 		VFS_UNLOCK_GIANT(vfslocked);
 		unp->un_dvp = NULLVP;
 	}
-	if (unp->un_path) {
+	if (unp->un_path != NULL) {
 		free(unp->un_path, M_UNIONFSPATH);
 		unp->un_path = NULL;
 	}
 
+	if (unp->un_hashtbl != NULL)
+		hashdestroy(unp->un_hashtbl, M_UNIONFSHASH, unp->un_hashmask);
+
 	LIST_FOREACH_SAFE(unsp, &(unp->un_unshead), uns_list, unsp_tmp) {
 		LIST_REMOVE(unsp, uns_list);
 		free(unsp, M_TEMP);
@@ -541,13 +710,16 @@
 	int		count, lockcnt;
 	struct vnode   *vp;
 	struct vnode   *lvp;
+	struct vnode   *dvp;
 
 	vp = UNIONFSTOV(unp);
 	lvp = unp->un_lowervp;
+	dvp = unp->un_dvp;
 
 	/*
 	 * lock update
 	 */
+	vkernref(uvp);
 	VI_LOCK(vp);
 	unp->un_uppervp = uvp;
 	vp->v_vnlock = uvp->v_vnlock;
@@ -557,6 +729,19 @@
 	VI_UNLOCK(vp);
 	for (count = 1; count < lockcnt; count++)
 		vn_lock(uvp, LK_EXCLUSIVE | LK_CANRECURSE | LK_RETRY, td);
+
+	/*
+	 * cache update
+	 */
+	if (unp->un_path != NULL && dvp != NULLVP && vp->v_type == VDIR) {
+		static struct unionfs_node_hashhead *hd;
+
+		VI_LOCK(dvp);
+		hd = unionfs_get_hashhead(dvp, unp->un_path);
+		LIST_REMOVE(unp, un_hash);
+		LIST_INSERT_HEAD(hd, unp, un_hash);
+		VI_UNLOCK(dvp);
+	}
 }
 
 /*
diff -urBN /usr/src.p19/sys/kern/vfs_subr.c /usr/src/sys/kern/vfs_subr.c
--- /usr/src.p19/sys/kern/vfs_subr.c	2007-07-09 19:27:30.000000000 +0900
+++ /usr/src/sys/kern/vfs_subr.c	2007-07-12 21:58:26.000000000 +0900
@@ -953,6 +953,7 @@
 	vp->v_tag = tag;
 	vp->v_op = vops;
 	v_incr_usecount(vp);
+	vp->v_kernusecount = 0;
 	vp->v_data = 0;
 #ifdef MAC
 	mac_init_vnode(vp);
@@ -2104,6 +2105,8 @@
 	VNASSERT(vp->v_writecount < vp->v_usecount || vp->v_usecount < 1, vp,
 	    ("vrele: missed vn_close"));
 
+	if (vp->v_usecount <= vp->v_kernusecount)
+		panic("vrele: kernel is referring to it");
 	if (vp->v_usecount > 1 || ((vp->v_iflag & VI_DOINGINACT) &&
 	    vp->v_usecount == 1)) {
 		v_decr_usecount(vp);
@@ -2143,6 +2146,32 @@
 }
 
 /*
+ * Increase the reference count of a vnode used by kernel.
+ */
+void
+vkernref(struct vnode *vp)
+{
+	VI_LOCK(vp);
+	if (vp->v_usecount <= vp->v_kernusecount)
+		panic("vkernref: vkernref call without vref");
+	vp->v_kernusecount++;
+	VI_UNLOCK(vp);
+}
+
+/*
+ * Decrease the reference count of a vnode used by kernel.
+ */
+void
+vkernrele(struct vnode *vp)
+{
+	VI_LOCK(vp);
+	if (vp->v_kernusecount < 1)
+		panic("vkernrele: negative kern ref count");
+	vp->v_kernusecount--;
+	VI_UNLOCK(vp);
+}
+
+/*
  * Release an already locked vnode.  This give the same effects as
  * unlock+vrele(), but takes less time and avoids releasing and
  * re-aquiring the lock (as vrele() acquires the lock internally.)
@@ -2373,7 +2402,8 @@
 		 *
 		 * If FORCECLOSE is set, forcibly close the vnode.
 		 */
-		if (vp->v_usecount == 0 || (flags & FORCECLOSE)) {
+		if ((vp->v_usecount == 0 || (flags & FORCECLOSE)) &&
+		    vp->v_kernusecount == 0) {
 			VNASSERT(vp->v_usecount == 0 ||
 			    (vp->v_type != VCHR && vp->v_type != VBLK), vp,
 			    ("device VNODE %p is FORCECLOSED", vp));
diff -urBN /usr/src.p19/sys/sys/vnode.h /usr/src/sys/sys/vnode.h
--- /usr/src.p19/sys/sys/vnode.h	2007-07-09 19:28:14.000000000 +0900
+++ /usr/src/sys/sys/vnode.h	2007-07-12 19:48:27.000000000 +0900
@@ -162,6 +162,7 @@
 	struct	lock *v_vnlock;			/* u pointer to vnode lock */
 	int	v_holdcnt;			/* i prevents recycling. */
 	int	v_usecount;			/* i ref count of users */
+	int	v_kernusecount;			/* i ref count of kernel */
 	u_long	v_iflag;			/* i vnode flags (see below) */
 	u_long	v_vflag;			/* v vnode flags */
 	int	v_writecount;			/* v ref count of writers */
@@ -706,6 +707,8 @@
 #define VOP_LOCK(vp, flags, td) VOP_LOCK1(vp, flags, td, __FILE__, __LINE__)
 
 
+void	vkernrele(struct vnode *vp);
+void	vkernref(struct vnode *vp);
 void	vput(struct vnode *vp);
 void	vrele(struct vnode *vp);
 void	vref(struct vnode *vp);

--------------070007080303020601050807--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?46EF6386.9050700>