Hi David, Moving the allocation to kmem_alloc_wired() before starting the APs makes sense given the allocator handoff you describe.
Could you confirm that the AP branch of mp_desc_init() uses mycpu for all CPU-specific state? Previously, it ran on the target AP after init_percpu(cpu). With this patch, it runs on the BSP before the AP reaches init_percpu(cpu). If the initialization depends only on the target CPU index, it would be helpful to mention that in the commit message. Best regards, Alperen Erkan On Fri, Nov 6, 2026 at 1:58 PM David Bidner <[email protected]> wrote: > 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 > > >
