Each AP called mp_desc_init() from cpu_setup(), which took the AP's
descriptor table (GDT, LDT, IDT and TSS) from init_alloc_aligned().
That is the boot allocator; once the VM system is up it no longer owns
any memory, so the AP wrote its table into pages the kernel had
already handed out.

Allocate the tables with kmem_alloc_wired() in start_other_cpus(),
before any AP is started, and let the APs only pick up their table.

When the APs start, the old code corrupted pv_head_table in a guest
with 2 GiB of RAM (38 bad entries at -smp 2, 114 at -smp 4, in a
QEMU/NVMM i386 PAE guest) and ended in a pmap fault or hang under
memory pressure within a minute at -smp 4.  With this change the
table is intact and 300 s runs of the same load (apt-get update plus
three parallel 200 MB downloads) are clean.
---
 i386/i386/mp_desc.c | 17 ++++++++++++-----
 i386/i386/mp_desc.h |  2 +-
 2 files changed, 13 insertions(+), 6 deletions(-)

diff --git a/i386/i386/mp_desc.c b/i386/i386/mp_desc.c
index 1343861c..8f2589b3 100644
--- a/i386/i386/mp_desc.c
+++ b/i386/i386/mp_desc.c
@@ -151,11 +151,15 @@ mp_desc_init(int mycpu)
        }
        else {
                /*
-                * Allocate tables for other CPUs
+                * The BSP allocates AP tables after VM initialization, before
+                * starting the APs.  The boot allocator no longer owns these
+                * pages and must not be used here.
                 */
-               if (!init_alloc_aligned(sizeof(struct mp_desc_table), &mem))
+               if (kmem_alloc_wired(kernel_map, &mem,
+                                    round_page(sizeof(struct mp_desc_table)))
+                   != KERN_SUCCESS)
                        panic("not enough memory for descriptor tables");
-               mpt = (struct mp_desc_table *)phystokv(mem);
+               mpt = (struct mp_desc_table *)mem;
 
                mp_desc_table[mycpu] = mpt;
                mp_ktss[mycpu] = &mpt->ktss;
@@ -236,8 +240,7 @@ cpu_setup(int cpu)
     printf("AP=(%u) paging done\n", cpu);
 
     init_percpu(cpu);
-    mp_desc_init(cpu);
-    printf("AP=(%u) mpdesc done\n", cpu);
+    assert(mp_desc_table[cpu] != NULL);
 
     ap_gdt_init(cpu);
     printf("AP=(%u) gdt done\n", cpu);
@@ -283,6 +286,10 @@ start_other_cpus(void)
        if (ncpus == 1)
          return;
 
+       /* Allocate while the BSP can use the VM allocator and before APs run. 
*/
+       for (int cpu = 1; cpu < ncpus; cpu++)
+               mp_desc_init(cpu);
+
        //Copy cpu initialization assembly routine
 #ifdef __i386__
        memcpy((void*) phystokv(apboot_addr), (void*) &apboot,
diff --git a/i386/i386/mp_desc.h b/i386/i386/mp_desc.h
index 92790171..2adce2b0 100644
--- a/i386/i386/mp_desc.h
+++ b/i386/i386/mp_desc.h
@@ -75,7 +75,7 @@ extern struct real_descriptor *mp_gdt[NCPUS];
 extern uint8_t solid_intstack[];
 
 /*
- * Each CPU calls this routine to set up its descriptor tables.
+ * The BSP calls this routine to set up each CPU's descriptor tables.
  */
 extern int mp_desc_init(int);
 
-- 
2.47.3


Reply via email to