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);

