Permalink
Browse files

kernel - Fix i386 wire_count panics

* Tracked down to a situation where a pmap structure is being dtor'd by
  the objcache simultaniously with a vm_page_protect() operation on
  a page table page's vm_page_t.

  (1) vm_page_protect() begins running, finds page table page to remove,
      removes the related pv_entry, but then gets stuck waiting for the
      pmap->pm_pteobj (vm_object token).

  (2) Exit on another thread simultaniously removes all remaining VM
      pages from the pmap.  However, due to #(1), there is still an
      active page table page in pmap->pm_pteobj that the exit code has
      no visibility to.

  (3) The related pmap is then dtor'd due to heavy fork/exec/exit load
      on the system.  The VM page is still present, vm_page_protect()
      is still stuck on the token (or hasn't gotten cpu back).

  (4) Nominal vm_object_terminate() destroys the page table page.

  (5) vm_page_protect() unblocks and tries to destroy the page.

  (6) BOOM.

* This fix places a barrier between the normal process exit code and the
  dtor which will block while a vm_page_protect() is active on the pmap.

* This time for sure, but if not we still know that the problem is related
  to this exit race.
  • Loading branch information...
1 parent 97216d2 commit 3321ee05e41cb26307055bd297c1de7fad3973ba Matthew Dillon committed Sep 26, 2012
Showing with 60 additions and 23 deletions.
  1. +60 −12 sys/platform/pc32/i386/pmap.c
  2. +0 −10 sys/platform/pc64/x86_64/pmap.c
  3. +0 −1 sys/vm/pmap.h
@@ -215,6 +215,10 @@ static vm_page_t pmap_page_lookup (vm_object_t object, vm_pindex_t pindex);
static void pmap_unuse_pt (pmap_t, vm_offset_t, vm_page_t, pmap_inval_info_t);
static vm_offset_t pmap_kmem_choose(vm_offset_t addr);
+static void pmap_hold(pmap_t pmap);
+static void pmap_drop(pmap_t pmap);
+static void pmap_wait(pmap_t pmap, int count);
+
static unsigned pdir4mb;
/*
@@ -1231,6 +1235,7 @@ pmap_puninit(pmap_t pmap)
{
vm_page_t p;
+ pmap_wait(pmap, -1);
KKASSERT(pmap->pm_active == 0);
if ((p = pmap->pm_pdirm) != NULL) {
KKASSERT(pmap->pm_pdir != NULL);
@@ -1626,6 +1631,45 @@ pmap_reference(pmap_t pmap)
}
}
+/*
+ * vm_token must be held
+ */
+static
+void
+pmap_hold(pmap_t pmap)
+{
+ ++pmap->pm_count;
+}
+
+/*
+ * vm_token must be held
+ */
+static
+void
+pmap_drop(pmap_t pmap)
+{
+ --pmap->pm_count;
+ if ((pmap->pm_count & 0x7FFFFFFF) == 0)
+ wakeup(pmap);
+}
+
+static
+void
+pmap_wait(pmap_t pmap, int count)
+{
+ lwkt_gettoken(&vm_token);
+ pmap->pm_count += count;
+ if (pmap->pm_count & 0x7FFFFFFF) {
+ while (pmap->pm_count & 0x7FFFFFFF) {
+ pmap->pm_count |= 0x80000000;
+ tsleep(pmap, 0, "pmapd", 0);
+ pmap->pm_count &= ~0x80000000;
+ kprintf("pmap_wait: race averted\n");
+ }
+ }
+ lwkt_reltoken(&vm_token);
+}
+
/***************************************************
* page management routines.
***************************************************/
@@ -1997,29 +2041,32 @@ pmap_remove(struct pmap *pmap, vm_offset_t sva, vm_offset_t eva)
* Removes this physical page from all physical maps in which it resides.
* Reflects back modify bits to the pager.
*
- * No requirements.
+ * vm_token must be held by caller.
*/
static void
pmap_remove_all(vm_page_t m)
{
struct pmap_inval_info info;
unsigned *pte, tpte;
pv_entry_t pv;
+ pmap_t pmap;
if (!pmap_initialized || (m->flags & PG_FICTITIOUS))
return;
pmap_inval_init(&info);
while ((pv = TAILQ_FIRST(&m->md.pv_list)) != NULL) {
- KKASSERT(pv->pv_pmap->pm_stats.resident_count > 0);
- --pv->pv_pmap->pm_stats.resident_count;
+ pmap = pv->pv_pmap;
+ KKASSERT(pmap->pm_stats.resident_count > 0);
+ --pmap->pm_stats.resident_count;
+ pmap_hold(pmap);
- pte = pmap_pte_quick(pv->pv_pmap, pv->pv_va);
- pmap_inval_interlock(&info, pv->pv_pmap, pv->pv_va);
+ pte = pmap_pte_quick(pmap, pv->pv_va);
+ pmap_inval_interlock(&info, pmap, pv->pv_va);
tpte = loadandclear(pte);
if (tpte & PG_W)
- pv->pv_pmap->pm_stats.wired_count--;
- pmap_inval_deinterlock(&info, pv->pv_pmap);
+ pmap->pm_stats.wired_count--;
+ pmap_inval_deinterlock(&info, pmap);
if (tpte & PG_A)
vm_page_flag_set(m, PG_REFERENCED);
KKASSERT(PHYS_TO_VM_PAGE(tpte) == m);
@@ -2043,17 +2090,18 @@ pmap_remove_all(vm_page_t m)
#endif
KKASSERT(pv == TAILQ_FIRST(&m->md.pv_list));
TAILQ_REMOVE(&m->md.pv_list, pv, pv_list);
- TAILQ_REMOVE(&pv->pv_pmap->pm_pvlist, pv, pv_plist);
- ++pv->pv_pmap->pm_generation;
+ TAILQ_REMOVE(&pmap->pm_pvlist, pv, pv_plist);
+ ++pmap->pm_generation;
m->md.pv_list_count--;
if (m->object)
atomic_add_int(&m->object->agg_pv_list_count, -1);
if (TAILQ_EMPTY(&m->md.pv_list))
vm_page_flag_clear(m, PG_MAPPED | PG_WRITEABLE);
- vm_object_hold(pv->pv_pmap->pm_pteobj);
- pmap_unuse_pt(pv->pv_pmap, pv->pv_va, pv->pv_ptem, &info);
- vm_object_drop(pv->pv_pmap->pm_pteobj);
+ vm_object_hold(pmap->pm_pteobj);
+ pmap_unuse_pt(pmap, pv->pv_va, pv->pv_ptem, &info);
+ vm_object_drop(pmap->pm_pteobj);
free_pv_entry(pv);
+ pmap_drop(pmap);
}
KKASSERT((m->flags & (PG_MAPPED|PG_WRITEABLE)) == 0);
pmap_inval_done(&info);
@@ -2360,16 +2360,6 @@ pmap_reference(pmap_t pmap)
}
}
-void
-pmap_drop(pmap_t pmap)
-{
- if (pmap != NULL) {
- lwkt_gettoken(&pmap->pm_token);
- --pmap->pm_count;
- lwkt_reltoken(&pmap->pm_token);
- }
-}
-
/***************************************************
* page management routines.
***************************************************/
View
@@ -184,7 +184,6 @@ void pmap_kmodify_nc(vm_offset_t va);
void pmap_kremove (vm_offset_t);
void pmap_kremove_quick (vm_offset_t);
void pmap_reference (pmap_t);
-void pmap_drop (pmap_t);
void pmap_remove (pmap_t, vm_offset_t, vm_offset_t);
void pmap_remove_pages (pmap_t, vm_offset_t, vm_offset_t);
void pmap_zero_page (vm_paddr_t);

0 comments on commit 3321ee0

Please sign in to comment.