summaryrefslogtreecommitdiffstats
path: root/sys/x86
diff options
context:
space:
mode:
authorneel <neel@FreeBSD.org>2015-03-14 00:30:41 +0000
committerneel <neel@FreeBSD.org>2015-03-14 00:30:41 +0000
commit771dc2cc82487fde44a71d30533ccde7bf753a8a (patch)
treeb7bd2d7ea1bcdd0788cff1e68a21b7910126728c /sys/x86
parent43c1b0e92f334dc7dd539cbebd72ecc170cbbf22 (diff)
downloadFreeBSD-src-771dc2cc82487fde44a71d30533ccde7bf753a8a.zip
FreeBSD-src-771dc2cc82487fde44a71d30533ccde7bf753a8a.tar.gz
Add x86 specific APIs 'lapic_ipi_alloc()' and 'lapic_ipi_free()' to allow IPI
vectors to be dynamically allocated. This allows kernel modules like vmm.ko to allocate unique IPI slots when loaded (as opposed to hard allocating one or more vectors). Also, reorganize the fixed IPI vectors to create a contiguous space for dynamic IPI allocation. Reviewed by: kib, jhb Differential Revision: https://reviews.freebsd.org/D2042
Diffstat (limited to 'sys/x86')
-rw-r--r--sys/x86/include/apicvar.h32
-rw-r--r--sys/x86/x86/local_apic.c60
-rw-r--r--sys/x86/xen/xen_apic.c19
3 files changed, 104 insertions, 7 deletions
diff --git a/sys/x86/include/apicvar.h b/sys/x86/include/apicvar.h
index 048b646..0ed65de 100644
--- a/sys/x86/include/apicvar.h
+++ b/sys/x86/include/apicvar.h
@@ -111,11 +111,8 @@
#define IPI_INVLPG (APIC_IPI_INTS + 2)
#define IPI_INVLRNG (APIC_IPI_INTS + 3)
#define IPI_INVLCACHE (APIC_IPI_INTS + 4)
-#ifdef __i386__
-#define IPI_LAZYPMAP (APIC_IPI_INTS + 5) /* Lazy pmap release. */
-#endif
/* Vector to handle bitmap based IPIs */
-#define IPI_BITMAP_VECTOR (APIC_IPI_INTS + 6)
+#define IPI_BITMAP_VECTOR (APIC_IPI_INTS + 5)
/* IPIs handled by IPI_BITMAP_VECTOR */
#define IPI_AST 0 /* Generate software trap. */
@@ -124,8 +121,15 @@
#define IPI_BITMAP_LAST IPI_HARDCLOCK
#define IPI_IS_BITMAPED(x) ((x) <= IPI_BITMAP_LAST)
-#define IPI_STOP (APIC_IPI_INTS + 7) /* Stop CPU until restarted. */
-#define IPI_SUSPEND (APIC_IPI_INTS + 8) /* Suspend CPU until restarted. */
+#define IPI_STOP (APIC_IPI_INTS + 6) /* Stop CPU until restarted. */
+#define IPI_SUSPEND (APIC_IPI_INTS + 7) /* Suspend CPU until restarted. */
+#ifdef __i386__
+#define IPI_LAZYPMAP (APIC_IPI_INTS + 8) /* Lazy pmap release. */
+#define IPI_DYN_FIRST (APIC_IPI_INTS + 9)
+#else
+#define IPI_DYN_FIRST (APIC_IPI_INTS + 8)
+#endif
+#define IPI_DYN_LAST (254) /* IPIs allocated at runtime */
/*
* IPI_STOP_HARD does not need to occupy a slot in the IPI vector space since
@@ -224,6 +228,8 @@ struct apic_ops {
void (*ipi_raw)(register_t, u_int);
void (*ipi_vectored)(u_int, int);
int (*ipi_wait)(int);
+ int (*ipi_alloc)(inthand_t *ipifunc);
+ void (*ipi_free)(int vector);
/* LVT */
int (*set_lvt_mask)(u_int, u_int, u_char);
@@ -397,6 +403,20 @@ lapic_ipi_wait(int delay)
}
static inline int
+lapic_ipi_alloc(inthand_t *ipifunc)
+{
+
+ return (apic_ops.ipi_alloc(ipifunc));
+}
+
+static inline void
+lapic_ipi_free(int vector)
+{
+
+ return (apic_ops.ipi_free(vector));
+}
+
+static inline int
lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked)
{
diff --git a/sys/x86/x86/local_apic.c b/sys/x86/x86/local_apic.c
index 51955fa..e0656ef 100644
--- a/sys/x86/x86/local_apic.c
+++ b/sys/x86/x86/local_apic.c
@@ -303,6 +303,8 @@ static int native_lapic_set_lvt_polarity(u_int apic_id, u_int lvt,
enum intr_polarity pol);
static int native_lapic_set_lvt_triggermode(u_int apic_id, u_int lvt,
enum intr_trigger trigger);
+static int native_lapic_ipi_alloc(inthand_t *ipifunc);
+static void native_lapic_ipi_free(int vector);
struct apic_ops apic_ops = {
.create = native_lapic_create,
@@ -329,6 +331,8 @@ struct apic_ops apic_ops = {
.ipi_raw = native_lapic_ipi_raw,
.ipi_vectored = native_lapic_ipi_vectored,
.ipi_wait = native_lapic_ipi_wait,
+ .ipi_alloc = native_lapic_ipi_alloc,
+ .ipi_free = native_lapic_ipi_free,
#endif
.set_lvt_mask = native_lapic_set_lvt_mask,
.set_lvt_mode = native_lapic_set_lvt_mode,
@@ -1761,4 +1765,60 @@ native_lapic_ipi_vectored(u_int vector, int dest)
}
#endif /* DETECT_DEADLOCK */
}
+
+/*
+ * 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 void
+native_lapic_ipi_free(int vector)
+{
+ struct gate_descriptor *ip;
+ long func;
+
+ KASSERT(vector >= IPI_DYN_FIRST && vector <= IPI_DYN_LAST,
+ ("%s: invalid vector %d", __func__, vector));
+
+ mtx_lock_spin(&icu_lock);
+ ip = &idt[vector];
+ func = (ip->gd_hioffset << 16) | ip->gd_looffset;
+ KASSERT(func != (uintptr_t)&IDTVEC(rsvd),
+ ("invalid idtfunc %#lx", func));
+ setidt(vector, &IDTVEC(rsvd), SDT_APICT, SEL_KPL, GSEL_APIC);
+ mtx_unlock_spin(&icu_lock);
+}
+
#endif /* SMP */
diff --git a/sys/x86/xen/xen_apic.c b/sys/x86/xen/xen_apic.c
index ed86734..8e83184 100644
--- a/sys/x86/xen/xen_apic.c
+++ b/sys/x86/xen/xen_apic.c
@@ -311,7 +311,22 @@ xen_pv_lapic_ipi_wait(int delay)
XEN_APIC_UNSUPPORTED;
return (0);
}
-#endif
+
+static int
+xen_pv_lapic_ipi_alloc(inthand_t *ipifunc)
+{
+
+ XEN_APIC_UNSUPPORTED;
+ return (-1);
+}
+
+static void
+xen_pv_lapic_ipi_free(int vector)
+{
+
+ XEN_APIC_UNSUPPORTED;
+}
+#endif /* SMP */
static int
xen_pv_lapic_set_lvt_mask(u_int apic_id, u_int lvt, u_char masked)
@@ -372,6 +387,8 @@ struct apic_ops xen_apic_ops = {
.ipi_raw = xen_pv_lapic_ipi_raw,
.ipi_vectored = xen_pv_lapic_ipi_vectored,
.ipi_wait = xen_pv_lapic_ipi_wait,
+ .ipi_alloc = xen_pv_lapic_ipi_alloc,
+ .ipi_free = xen_pv_lapic_ipi_free,
#endif
.set_lvt_mask = xen_pv_lapic_set_lvt_mask,
.set_lvt_mode = xen_pv_lapic_set_lvt_mode,
OpenPOWER on IntegriCloud