kernel - Fix one-cycle MP race in vshouldmsync()
authorMatthew Dillon <dillon@apollo.backplane.com>
Sun, 12 Sep 2010 17:30:38 +0000 (10:30 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Sun, 12 Sep 2010 17:30:38 +0000 (10:30 -0700)
* vshouldmsync() is the mntvnode fast function, which is called without
  any vnode lock.  vp->v_object can thus get ripped out from under the
  scan function.

  Hold vmobj_token through the scan so any pointer accessed via
  v_object remains stable (even if no longer related to the vnode
  due to the race).

Reported-by: swildner
sys/kern/vfs_mount.c
sys/kern/vfs_subr.c

index 0ccb903..47fb214 100644 (file)
@@ -986,6 +986,11 @@ insmntque(struct vnode *vp, struct mount *mp)
  * arbitrarily block.  The scanning code guarentees consistency of operation
  * even if the slow function deletes or moves the node, or blocks and some
  * other thread deletes or moves the node.
+ *
+ * NOTE: We hold vmobj_token to prevent a VM object from being destroyed
+ *      out from under the fastfunc()'s vnode test.  It will not prevent
+ *      v_object from getting NULL'd out but it will ensure that the
+ *      pointer (if we race) will remain stable.
  */
 int
 vmntvnodescan(
@@ -1003,6 +1008,7 @@ vmntvnodescan(
        int count = 0;
 
        lwkt_gettoken(&mntvnode_token);
+       lwkt_gettoken(&vmobj_token);
 
        /*
         * If asked to do one pass stop after iterating available vnodes.
@@ -1121,6 +1127,7 @@ next:
                        info.vp = TAILQ_NEXT(vp, v_nmntvnodes);
        }
        TAILQ_REMOVE(&mntvnodescan_list, &info, entry);
+       lwkt_reltoken(&vmobj_token);
        lwkt_reltoken(&mntvnode_token);
        return(r);
 }
index 32a333b..398820e 100644 (file)
@@ -153,16 +153,22 @@ rb_buf_compare(struct buf *b1, struct buf *b2)
 
 /*
  * Returns non-zero if the vnode is a candidate for lazy msyncing.
+ *
+ * NOTE: v_object is not stable (this scan can race), however the
+ *      mntvnodescan code holds vmobj_token so any VM object we
+ *      do find will remain stable storage.
  */
 static __inline int
 vshouldmsync(struct vnode *vp)
 {
+       vm_object_t object;
+
        if (vp->v_auxrefs != 0 || vp->v_sysref.refcnt > 0)
                return (0);             /* other holders */
-       if (vp->v_object &&
-           (vp->v_object->ref_count || vp->v_object->resident_page_count)) {
-               return (0);
-       }
+       object = vp->v_object;
+       cpu_ccfence();
+       if (object && (object->ref_count || object->resident_page_count))
+               return(0);
        return (1);
 }