From owner-freebsd-current@FreeBSD.ORG Tue Sep 18 05:35:09 2007 Return-Path: Delivered-To: freebsd-current@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 00F6A16A418 for ; Tue, 18 Sep 2007 05:35:09 +0000 (UTC) (envelope-from daichi@ongs.co.jp) Received: from natial.ongs.co.jp (natial.ongs.co.jp [202.216.232.58]) by mx1.freebsd.org (Postfix) with ESMTP id BBA8A13C48A for ; Tue, 18 Sep 2007 05:35:07 +0000 (UTC) (envelope-from daichi@ongs.co.jp) Received: from parancell.ongs.co.jp (dullmdaler.ongs.co.jp [202.216.232.62]) by natial.ongs.co.jp (Postfix) with ESMTP id 723CD244C19; Tue, 18 Sep 2007 14:35:02 +0900 (JST) Message-ID: <46EF6386.9050700@ongs.co.jp> Date: Tue, 18 Sep 2007 14:35:02 +0900 From: Daichi GOTO User-Agent: Thunderbird 2.0.0.6 (X11/20070803) MIME-Version: 1.0 To: Kurt Jaeger , freebsd-stable@freebsd.org, FreeBSD Current References: <200709141103.18612.h.schmalzbauer@omnisec.de> <20070914110024.G14481@fledge.watson.org> <46EBEA18.3080800@ongs.co.jp> <20070915170104.GF2061@home.c0mplx.org> In-Reply-To: <20070915170104.GF2061@home.c0mplx.org> Content-Type: multipart/mixed; boundary="------------070007080303020601050807" Cc: Subject: Re: Unionfs patchset p19 commit? X-BeenThere: freebsd-current@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Discussions about the use of FreeBSD-current List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 18 Sep 2007 05:35:09 -0000 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 +#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 +#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--