FreeBSD-current/mipsのSMP実装#仮想メモリ編
static void pmap_invalidate_all(pmap_t pmap) { #ifdef SMP smp_rendezvous(0, pmap_invalidate_all_action, 0, (void *)pmap); } static void pmap_invalidate_all_action(void *arg) { pmap_t pmap = (pmap_t)arg; #endif if (pmap->pm_active & PCPU_GET(cpumask)) { pmap_TLB_invalidate_all(); } else pmap->pm_asid[PCPU_GET(cpuid)].gen = 0; }
static __inline void pmap_invalidate_page(pmap_t pmap, vm_offset_t va) { #ifdef SMP struct pmap_invalidate_page_arg arg; arg.pmap = pmap; arg.va = va; smp_rendezvous(0, pmap_invalidate_page_action, 0, (void *)&arg); } static void pmap_invalidate_page_action(void *arg) { pmap_t pmap = ((struct pmap_invalidate_page_arg *)arg)->pmap; vm_offset_t va = ((struct pmap_invalidate_page_arg *)arg)->va; #endif if (is_kernel_pmap(pmap)) { pmap_TLB_invalidate_kernel(va); return; } if (pmap->pm_asid[PCPU_GET(cpuid)].gen != PCPU_GET(asid_generation)) return; else if (!(pmap->pm_active & PCPU_GET(cpumask))) { pmap->pm_asid[PCPU_GET(cpuid)].gen = 0; return; } va = pmap_va_asid(pmap, (va & ~PGOFSET)); mips_TBIS(va); }
void pmap_update_page(pmap_t pmap, vm_offset_t va, pt_entry_t pte) { #ifdef SMP struct pmap_update_page_arg arg; arg.pmap = pmap; arg.va = va; arg.pte = pte; smp_rendezvous(0, pmap_update_page_action, 0, (void *)&arg); } static void pmap_update_page_action(void *arg) { pmap_t pmap = ((struct pmap_update_page_arg *)arg)->pmap; vm_offset_t va = ((struct pmap_update_page_arg *)arg)->va; pt_entry_t pte = ((struct pmap_update_page_arg *)arg)->pte; #endif if (is_kernel_pmap(pmap)) { pmap_TLB_update_kernel(va, pte); return; } if (pmap->pm_asid[PCPU_GET(cpuid)].gen != PCPU_GET(asid_generation)) return; else if (!(pmap->pm_active & PCPU_GET(cpumask))) { pmap->pm_asid[PCPU_GET(cpuid)].gen = 0; return; } va = pmap_va_asid(pmap, va); MachTLBUpdate(va, pte); }
linux/mipsのcacheまわりの部分と同じように、SMPの時はIPIを使って同じ処理を一斉に実行させている。
smp_rendezvous()がその関数になっている。
void smp_rendezvous_cpus(cpumask_t map, void (* setup_func)(void *), void (* action_func)(void *), void (* teardown_func)(void *), void *arg) { int i, ncpus = 0; if (!smp_started) { if (setup_func != NULL) setup_func(arg); if (action_func != NULL) action_func(arg); if (teardown_func != NULL) teardown_func(arg); return; } for (i = 0; i < mp_maxid; i++) if (((1 << i) & map) != 0 && !CPU_ABSENT(i)) ncpus++; /* obtain rendezvous lock */ mtx_lock_spin(&smp_ipi_mtx); /* set static function pointers */ smp_rv_ncpus = ncpus; smp_rv_setup_func = setup_func; smp_rv_action_func = action_func; smp_rv_teardown_func = teardown_func; smp_rv_func_arg = arg; smp_rv_waiters[1] = 0; smp_rv_waiters[2] = 0; atomic_store_rel_int(&smp_rv_waiters[0], 0); /* signal other processors, which will enter the IPI with interrupts off */ ipi_selected(map & ~(1 << curcpu), IPI_RENDEZVOUS); /* Check if the current CPU is in the map */ if ((map & (1 << curcpu)) != 0) smp_rendezvous_action(); if (teardown_func == smp_no_rendevous_barrier) while (atomic_load_acq_int(&smp_rv_waiters[2]) < ncpus) cpu_spinwait(); /* release lock */ mtx_unlock_spin(&smp_ipi_mtx); } void smp_rendezvous(void (* setup_func)(void *), void (* action_func)(void *), void (* teardown_func)(void *), void *arg) { smp_rendezvous_cpus(all_cpus, setup_func, action_func, teardown_func, arg); }
IPI受け取り時に実行したい関数を登録してからIPIを送信している。