NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
Re: kern/41374: getnewvnode deadlock
The following reply was made to PR kern/41374; it has been noted by GNATS.
From: yamt%mwd.biglobe.ne.jp@localhost (YAMAMOTO Takashi)
To: ad%netbsd.org@localhost
Cc: gnats-bugs%NetBSD.org@localhost, kern-bug-people%netbsd.org@localhost,
gnats-admin%netbsd.org@localhost,
netbsd-bugs%netbsd.org@localhost
Subject: Re: kern/41374: getnewvnode deadlock
Date: Tue, 12 May 2009 11:04:21 +0000 (UTC)
hi,
> On Sun, May 10, 2009 at 09:44:41AM +0000, YAMAMOTO Takashi wrote:
>
>> > How about making XLOCK
>> > a flag bit in v_useconut?
>>
>> it sounds like a good idea.
>
> On second thought what you propose looks fine to me. Putting a flag bit into
> v_usecount would have the advantage that no memory barrier would be required
> in vtryget().
>
>> @@ -1783,8 +1794,8 @@ vclean(vnode_t *vp, int flags)
>> KASSERT(vp->v_usecount != 0);
>>
>> /* If cleaning is already in progress wait until done and return. */
>> - if (vp->v_iflag & VI_XLOCK) {
>> - vwait(vp, VI_XLOCK);
>> + if (vp->v_iflag & VI_CLEANING) {
>> + vwait(vp, VI_CLEANING);
>> return;
>> }
>
> Can't this allow a concurrent vclean(), e.g. via revoke or forced unmount?
VI_CLEANING is to check for concurrent vclean.
anyway i prefer your suggestion of putting a bit into v_usecount and
implemented it. see below.
>> @@ -464,6 +464,15 @@ cache_lookup_raw(struct vnode *dvp, stru
>> return ENOENT;
>> }
>> if (vtryget(vp)) {
>> + membar_consumer();
>> + if ((vp->v_iflag & VI_XLOCK) != 0) {
>> + mutex_exit(&ncp->nc_lock);
>> + mutex_exit(&cpup->cpu_lock);
>> + vrele(vp);
>> + COUNT(cpup->cpu_stats, ncs_falsehits);
>> + *vpp = NULL;
>> + return -1;
>> + }
>> mutex_exit(&ncp->nc_lock);
>> mutex_exit(&cpup->cpu_lock);
>
> I spent a while today trying to remember why vget() does not do vtryget().
> Of course it's due to VI_FREEING. I wonder if we could change the order of
> events in ufs_inactive() to this, allowing us to remove VI_FREEING (I don't
> know what ZFS will require):
>
> UFS_UPDATE(vp, ...);
> ufs_ihashrem(ip);
> UFS_VFREE(vp, ...);
>
> I'm not yet sure if fsck can handle it the other way around.
wapbl might need some tweaks, too, i guess.
YAMAMOTO Takashi
Index: sys/vnode.h
===================================================================
RCS file: /cvsroot/src/sys/sys/vnode.h,v
retrieving revision 1.191.4.2
diff -u -p -r1.191.4.2 vnode.h
--- sys/vnode.h 4 May 2009 08:14:36 -0000 1.191.4.2
+++ sys/vnode.h 12 May 2009 11:02:37 -0000
@@ -250,6 +250,12 @@ typedef struct vnode vnode_t;
#define VSIZENOTSET ((voff_t)-1)
/*
+ * v_usecount
+ */
+#define VC_XLOCK 0x80000000
+#define VC_MASK 0x7fffffff
+
+/*
* Vnode attributes. A field value of VNOVAL represents a field whose value
* is unavailable (getattr) or which is not to be changed (setattr).
*/
Index: kern/vfs_subr.c
===================================================================
RCS file: /cvsroot/src/sys/kern/vfs_subr.c,v
retrieving revision 1.336.4.2
diff -u -p -r1.336.4.2 vfs_subr.c
--- kern/vfs_subr.c 4 May 2009 08:13:49 -0000 1.336.4.2
+++ kern/vfs_subr.c 12 May 2009 11:02:37 -0000
@@ -361,8 +361,10 @@ try_nextlist:
* before doing this. If the vnode gains another reference while
* being cleaned out then we lose - retry.
*/
- atomic_inc_uint(&vp->v_usecount);
+ atomic_add_int(&vp->v_usecount, 1 + VC_XLOCK);
vclean(vp, DOCLOSE);
+ KASSERT(vp->v_usecount >= 1 + VC_XLOCK);
+ atomic_add_int(&vp->v_usecount, -VC_XLOCK);
if (vp->v_usecount == 1) {
/* We're about to dirty it. */
vp->v_iflag &= ~VI_CLEAN;
@@ -1229,7 +1231,7 @@ vtryget(vnode_t *vp)
return false;
}
for (use = vp->v_usecount;; use = next) {
- if (use == 0) {
+ if (use == 0 || __predict_false((use & VC_XLOCK) != 0)) {
/* Need interlock held if first reference. */
return false;
}
@@ -1318,9 +1320,10 @@ vtryrele(vnode_t *vp)
u_int use, next;
for (use = vp->v_usecount;; use = next) {
- if (use == 1) {
+ if (use == 1) {
return false;
}
+ KASSERT((use & VC_MASK) > 1);
next = atomic_cas_uint(&vp->v_usecount, use, use - 1);
if (__predict_true(next == use)) {
return true;
Home |
Main Index |
Thread Index |
Old Index