Module Name: src Committed By: hannken Date: Fri Nov 29 14:58:55 UTC 2013
Modified Files: src/sys/kern: vfs_vnode.c Log Message: Change vrelel() to mark the vnode as changing after it has aquired the vnode lock but before it calls VOP_INACTIVE(). Should fix the race between layer_node_find() trying to vget(, LK_NOWAIT) a locked vnode when vrelel() marked it as changing and wants its lock. PR kern/48411 (repeatable SMP crashes in amd64-current) To generate a diff of this commit: cvs rdiff -u -r1.26 -r1.27 src/sys/kern/vfs_vnode.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/kern/vfs_vnode.c diff -u src/sys/kern/vfs_vnode.c:1.26 src/sys/kern/vfs_vnode.c:1.27 --- src/sys/kern/vfs_vnode.c:1.26 Sat Nov 23 13:46:22 2013 +++ src/sys/kern/vfs_vnode.c Fri Nov 29 14:58:55 2013 @@ -1,4 +1,4 @@ -/* $NetBSD: vfs_vnode.c,v 1.26 2013/11/23 13:46:22 hannken Exp $ */ +/* $NetBSD: vfs_vnode.c,v 1.27 2013/11/29 14:58:55 hannken Exp $ */ /*- * Copyright (c) 1997-2011 The NetBSD Foundation, Inc. @@ -116,7 +116,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.26 2013/11/23 13:46:22 hannken Exp $"); +__KERNEL_RCSID(0, "$NetBSD: vfs_vnode.c,v 1.27 2013/11/29 14:58:55 hannken Exp $"); #define _VFS_VNODE_PRIVATE @@ -606,10 +606,6 @@ vrelel(vnode_t *vp, int flags) } KASSERT((vp->v_iflag & VI_XLOCK) == 0); - if ((flags & VRELEL_CHANGING_SET) == 0) { - KASSERT((vp->v_iflag & VI_CHANGING) == 0); - vp->v_iflag |= VI_CHANGING; - } #ifdef DIAGNOSTIC if ((vp->v_type == VBLK || vp->v_type == VCHR) && @@ -654,13 +650,14 @@ vrelel(vnode_t *vp, int flags) */ if (__predict_false(vtryrele(vp))) { VOP_UNLOCK(vp); - KASSERT((vp->v_iflag & VI_CHANGING) != 0); - vp->v_iflag &= ~VI_CHANGING; - cv_broadcast(&vp->v_cv); + if ((flags & VRELEL_CHANGING_SET) != 0) { + KASSERT((vp->v_iflag & VI_CHANGING) != 0); + vp->v_iflag &= ~VI_CHANGING; + cv_broadcast(&vp->v_cv); + } mutex_exit(vp->v_interlock); return; } - mutex_exit(vp->v_interlock); defer = false; } else if ((vp->v_iflag & VI_LAYER) != 0) { /* @@ -675,10 +672,10 @@ vrelel(vnode_t *vp, int flags) error = vn_lock(vp, LK_EXCLUSIVE | LK_NOWAIT); if (error != 0) { defer = true; - mutex_enter(vp->v_interlock); } else { defer = false; } + mutex_enter(vp->v_interlock); } if (defer) { @@ -687,18 +684,26 @@ vrelel(vnode_t *vp, int flags) * clean it here. We donate it our last reference. */ KASSERT(mutex_owned(vp->v_interlock)); - KASSERT((vp->v_iflag & VI_CHANGING) != 0); - vp->v_iflag &= ~VI_CHANGING; + if ((flags & VRELEL_CHANGING_SET) != 0) { + KASSERT((vp->v_iflag & VI_CHANGING) != 0); + vp->v_iflag &= ~VI_CHANGING; + cv_broadcast(&vp->v_cv); + } mutex_enter(&vrele_lock); TAILQ_INSERT_TAIL(&vrele_list, vp, v_freelist); if (++vrele_pending > (desiredvnodes >> 8)) cv_signal(&vrele_cv); mutex_exit(&vrele_lock); - cv_broadcast(&vp->v_cv); mutex_exit(vp->v_interlock); return; } + if ((flags & VRELEL_CHANGING_SET) == 0) { + KASSERT((vp->v_iflag & VI_CHANGING) == 0); + vp->v_iflag |= VI_CHANGING; + } + mutex_exit(vp->v_interlock); + /* * The vnode can gain another reference while being * deactivated. If VOP_INACTIVE() indicates that @@ -739,6 +744,11 @@ vrelel(vnode_t *vp, int flags) vclean(vp); } KASSERT(vp->v_usecount > 0); + } else { /* vnode was already clean */ + if ((flags & VRELEL_CHANGING_SET) == 0) { + KASSERT((vp->v_iflag & VI_CHANGING) == 0); + vp->v_iflag |= VI_CHANGING; + } } if (atomic_dec_uint_nv(&vp->v_usecount) != 0) {