kernel - Fix resident_count problems with kernel_pmap & related crashes
authorMatthew Dillon <dillon@apollo.backplane.com>
Fri, 30 Apr 2010 02:48:58 +0000 (19:48 -0700)
committerMatthew Dillon <dillon@apollo.backplane.com>
Fri, 30 Apr 2010 18:04:38 +0000 (11:04 -0700)
* kernel_pmap.pm_stats.resident_count could get out of sync due to the
  lwbuf code calling kmem_free() on KVM with pages that were entered
  via pmap_kenter().  Delete the pages before calling kmem_free().

  This in turn caused various failures and kernel panics due to conditionals
  based on resident_count == 0.  Heavy sendfile() activity could trigger
  the bug but it was taking a few days for the resident_count to decrement
  to zero.

* Fix a similar issue in zdestroy() on ZONE_INTERRUPT zones.  Nothing calls
  this routine so it was not a vector for crashes.

* Document the case.

sys/cpu/i386/misc/lwbuf.c
sys/vm/vm_kern.c
sys/vm/vm_zone.c

index 535fe4c..430c049 100644 (file)
@@ -95,6 +95,12 @@ lwbuf_cache_ctor(void *obj, void *pdata, int ocflags)
     return (TRUE);
 }
 
+/*
+ * Destructor for lwb.  Note that we must remove any pmap entries
+ * created with pmap_kenter() to prevent them from being misinterpreted
+ * as managed pages which would cause kernel_pmap.pm_stats.resident_count
+ * to get out of whack.
+ */
 static void
 lwbuf_cache_dtor(void *obj, void *pdata)
 {
@@ -102,6 +108,7 @@ lwbuf_cache_dtor(void *obj, void *pdata)
 
     KKASSERT(lwb->kva != 0);
     get_mplock();
+    pmap_kremove_quick(lwb->kva);
     kmem_free(&kernel_map, lwb->kva, PAGE_SIZE);
     rel_mplock();
     lwb->kva = 0;
index 817861c..1c35838 100644 (file)
@@ -233,6 +233,11 @@ kmem_alloc3(vm_map_t map, vm_size_t size, int kmflags)
  *     with kmem_alloc, and return the physical pages
  *     associated with that region.
  *
+ *     WARNING!  If the caller entered pages into the region using
+ *     pmap_kenter() it must remove the pages using pmap_kremove[_quick]()
+ *     before freeing the underlying kmem, otherwise resident_count will
+ *     be mistabulated.
+ *
  *     This routine may not block on kernel maps.
  */
 void
index 06f29e9..c4084e9 100644 (file)
@@ -325,10 +325,18 @@ zdestroy(vm_zone_t z)
         */
        if (z->zflags & ZONE_INTERRUPT) {
                /*
+                * Pages mapped via pmap_kenter() must be removed from the
+                * kernel_pmap() before calling kmem_free() to avoid issues
+                * with kernel_pmap.pm_stats.resident_count.
+                */
+               pmap_qremove(z->zkva, z->zpagemax);
+
+               /*
                 * Free the mapping.
                 */
                kmem_free(&kernel_map, z->zkva, z->zpagemax*PAGE_SIZE);
                atomic_subtract_int(&zone_kmem_kvaspace, z->zpagemax*PAGE_SIZE);
+
                /*
                 * Free the backing object and physical pages.
                 */