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を送信している。