kernel - MPSAFE stabilization
authorMatthew Dillon <dillon@apollo.backplane.com>
Mon, 28 Dec 2009 17:15:25 +0000 (09:15 -0800)
committerMatthew Dillon <dillon@apollo.backplane.com>
Mon, 28 Dec 2009 17:15:25 +0000 (09:15 -0800)
* Fix for 'panic: sysref_activate: bad count 00000002'.  Mistakenly
  assumed the lockmgr lock was sufficient protection but forgot it
  might be acquired LK_SHARED.  Extend v_spinlock protection to fix
  the problem.

sys/kern/kern_sysref.c
sys/kern/vfs_lock.c

index 93b84fd..705dac3 100644 (file)
@@ -266,6 +266,8 @@ sysref_dtor(void *data, void *privdata)
  * so -0x40000000 (during initialization) will translate to a ref count of 1.
  * Any references made during initialization will translate to additional
  * positive ref counts.
+ *
+ * MPSAFE
  */
 void
 sysref_activate(struct sysref *sr)
index 0c8852a..2731b54 100644 (file)
@@ -453,6 +453,9 @@ vget(struct vnode *vp, int flags)
         * Reference the structure and then acquire the lock.  0->1
         * transitions and refs during termination are allowed here so
         * call sysref directly.
+        *
+        * NOTE: The requested lock might be a shared lock and does
+        *       not protect our access to the refcnt or other fields.
         */
        sysref_get(&vp->v_sysref);
        if ((error = vn_lock(vp, flags)) != 0) {
@@ -477,25 +480,27 @@ vget(struct vnode *vp, int flags)
                 * In the VFREE/VCACHED case we have to throw away the
                 * sysref that was earmarking those cases and preventing
                 * the vnode from being destroyed.  Our sysref is still held.
+                *
+                * The spinlock is our only real protection here.
                 */
                spin_lock_wr(&vp->v_spinlock);
                if (vp->v_flag & VFREE) {
                        __vbusy(vp);
+                       sysref_activate(&vp->v_sysref);
                        spin_unlock_wr(&vp->v_spinlock);
                        sysref_put(&vp->v_sysref);
-                       sysref_activate(&vp->v_sysref);
                } else if (vp->v_flag & VCACHED) {
                        _vclrflags(vp, VCACHED);
+                       sysref_activate(&vp->v_sysref);
                        spin_unlock_wr(&vp->v_spinlock);
                        sysref_put(&vp->v_sysref);
-                       sysref_activate(&vp->v_sysref);
                } else {
-                       spin_unlock_wr(&vp->v_spinlock);
                        if (sysref_isinactive(&vp->v_sysref)) {
                                sysref_activate(&vp->v_sysref);
                                kprintf("Warning vp %p reactivation race\n",
                                        vp);
                        }
+                       spin_unlock_wr(&vp->v_spinlock);
                }
                _vclrflags(vp, VINACTIVE);
                error = 0;