FreeBSD/sys/x86/x86/local_apic.c
/*
 * Since the IDT is shared by all CPUs the IPI slot update needs to be globally
 * visible.
 *
 * Consider the case where an IPI is generated immediately after allocation:
 *     vector = lapic_ipi_alloc(ipifunc);
 *     ipi_selected(other_cpus, vector);
 *
 * In xAPIC mode a write to ICR_LO has serializing semantics because the
 * APIC page is mapped as an uncached region. In x2APIC mode there is an
 * explicit 'mfence' before the ICR MSR is written. Therefore in both cases
 * the IDT slot update is globally visible before the IPI is delivered.
 */
Since the IDT is shared by all CPUs the IPI slot update needs to be globally
visible.

Consider the case where an IPI is generated immediately after allocation:
vector = lapic_ipi_alloc(ipifunc);
ipi_selected(other_cpus, vector).

In xAPIC mode a write to ICR_LO has serializing semantics because the
APIC page is mapped as an uncached region. In x2APIC mode there is an
explicit 'mfence' before the ICR MSR is written. Therefore in both cases
the IDT slot update is globally visible before the IPI is delivered.
Since the IDT is shared by all CPUs the IPI slot update needs to be globally visible . Consider the case where an IPI is generated immediately after allocation : vector = lapic_ipi_alloc ( ipifunc ) ; ipi_selected ( other_cpus vector ) . In xAPIC mode a write to ICR_LO has serializing semantics because the APIC page is mapped as an uncached region . In x2APIC mode there is an explicit ' mfence ' before the ICR MSR is written . Therefore in both cases the IDT slot update is globally visible before the IPI is delivered .
static int
native_lapic_ipi_alloc(inthand_t *ipifunc)
{
	struct gate_descriptor *ip;
	long func;
	int idx, vector;
	KASSERT(ipifunc != &IDTVEC(rsvd), ("invalid ipifunc %p", ipifunc));
	vector = -1;
	mtx_lock_spin(&icu_lock);
	for (idx = IPI_DYN_FIRST; idx <= IPI_DYN_LAST; idx++) {
		ip = &idt[idx];
		func = (ip->gd_hioffset << 16) | ip->gd_looffset;
		if (func == (uintptr_t)&IDTVEC(rsvd)) {
			vector = idx;
			setidt(vector, ipifunc, SDT_APIC, SEL_KPL, GSEL_APIC);
			break;
		}
	}
	mtx_unlock_spin(&icu_lock);
	return (vector);
}
static int native_lapic_ipi_alloc ( inthand_t * ipifunc ) { struct gate_descriptor * ip ; long func ; int idx , vector ; KASSERT ( ipifunc != & IDTVEC ( rsvd ) , ( " " , ipifunc ) ) ; vector = -1 ; mtx_lock_spin ( & icu_lock ) ; for ( idx = IPI_DYN_FIRST ; idx <= IPI_DYN_LAST ; idx ++ ) { ip = & idt [ idx ] ; func = ( ip -> gd_hioffset << 16 ) | ip -> gd_looffset ; if ( func == ( uintptr_t ) & IDTVEC ( rsvd ) ) { vector = idx ; setidt ( vector , ipifunc , SDT_APIC , SEL_KPL , GSEL_APIC ) ; break ; } } mtx_unlock_spin ( & icu_lock ) ; return ( vector ) ; }
Since the IDT is shared by all CPUs the IPI slot update needs to be globally
 visible.
Since the IDT is shared by all CPUs the IPI slot update needs to be globally visible .