summaryrefslogtreecommitdiffstats
path: root/sys/x86/xen/xen_intr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/x86/xen/xen_intr.c')
-rw-r--r--sys/x86/xen/xen_intr.c135
1 files changed, 125 insertions, 10 deletions
diff --git a/sys/x86/xen/xen_intr.c b/sys/x86/xen/xen_intr.c
index 54a6be6..b94f8d9 100644
--- a/sys/x86/xen/xen_intr.c
+++ b/sys/x86/xen/xen_intr.c
@@ -120,7 +120,7 @@ struct xenisrc {
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
static void xen_intr_suspend(struct pic *);
-static void xen_intr_resume(struct pic *);
+static void xen_intr_resume(struct pic *, bool suspend_cancelled);
static void xen_intr_enable_source(struct intsrc *isrc);
static void xen_intr_disable_source(struct intsrc *isrc, int eoi);
static void xen_intr_eoi_source(struct intsrc *isrc);
@@ -334,7 +334,7 @@ xen_intr_release_isrc(struct xenisrc *isrc)
evtchn_cpu_mask_port(isrc->xi_cpu, isrc->xi_port);
evtchn_cpu_unmask_port(0, isrc->xi_port);
- if (isrc->xi_close != 0) {
+ if (isrc->xi_close != 0 && is_valid_evtchn(isrc->xi_port)) {
struct evtchn_close close = { .port = isrc->xi_port };
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close))
panic("EVTCHNOP_close failed");
@@ -408,6 +408,7 @@ xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port,
return (error);
}
*isrcp = isrc;
+ evtchn_unmask_port(local_port);
return (0);
}
@@ -571,6 +572,9 @@ xen_intr_init(void *dummy __unused)
struct xen_intr_pcpu_data *pcpu;
int i;
+ if (!xen_domain())
+ return (0);
+
mtx_init(&xen_intr_isrc_lock, "xen-irq-lock", NULL, MTX_DEF);
/*
@@ -602,20 +606,116 @@ xen_intr_suspend(struct pic *unused)
{
}
+static void
+xen_rebind_ipi(struct xenisrc *isrc)
+{
+#ifdef SMP
+ int cpu = isrc->xi_cpu;
+ int acpi_id = pcpu_find(cpu)->pc_acpi_id;
+ int error;
+ struct evtchn_bind_ipi bind_ipi = { .vcpu = acpi_id };
+
+ error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi,
+ &bind_ipi);
+ if (error != 0)
+ panic("unable to rebind xen IPI: %d", error);
+
+ isrc->xi_port = bind_ipi.port;
+ isrc->xi_cpu = 0;
+ xen_intr_port_to_isrc[bind_ipi.port] = isrc;
+
+ error = xen_intr_assign_cpu(&isrc->xi_intsrc,
+ cpu_apic_ids[cpu]);
+ if (error)
+ panic("unable to bind xen IPI to CPU#%d: %d",
+ cpu, error);
+
+ evtchn_unmask_port(bind_ipi.port);
+#else
+ panic("Resume IPI event channel on UP");
+#endif
+}
+
+static void
+xen_rebind_virq(struct xenisrc *isrc)
+{
+ int cpu = isrc->xi_cpu;
+ int acpi_id = pcpu_find(cpu)->pc_acpi_id;
+ int error;
+ struct evtchn_bind_virq bind_virq = { .virq = isrc->xi_virq,
+ .vcpu = acpi_id };
+
+ error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq,
+ &bind_virq);
+ if (error != 0)
+ panic("unable to rebind xen VIRQ#%d: %d", isrc->xi_virq, error);
+
+ isrc->xi_port = bind_virq.port;
+ isrc->xi_cpu = 0;
+ xen_intr_port_to_isrc[bind_virq.port] = isrc;
+
+#ifdef SMP
+ error = xen_intr_assign_cpu(&isrc->xi_intsrc,
+ cpu_apic_ids[cpu]);
+ if (error)
+ panic("unable to bind xen VIRQ#%d to CPU#%d: %d",
+ isrc->xi_virq, cpu, error);
+#endif
+
+ evtchn_unmask_port(bind_virq.port);
+}
+
/**
* Return this PIC to service after being suspended.
*/
static void
-xen_intr_resume(struct pic *unused)
+xen_intr_resume(struct pic *unused, bool suspend_cancelled)
{
- u_int port;
+ shared_info_t *s = HYPERVISOR_shared_info;
+ struct xenisrc *isrc;
+ u_int isrc_idx;
+ int i;
- /*
- * Mask events for all ports. They will be unmasked after
- * drivers have re-registered their handlers.
- */
- for (port = 0; port < NR_EVENT_CHANNELS; port++)
- evtchn_mask_port(port);
+ if (suspend_cancelled)
+ return;
+
+ /* Reset the per-CPU masks */
+ CPU_FOREACH(i) {
+ struct xen_intr_pcpu_data *pcpu;
+
+ pcpu = DPCPU_ID_PTR(i, xen_intr_pcpu);
+ memset(pcpu->evtchn_enabled,
+ i == 0 ? ~0 : 0, sizeof(pcpu->evtchn_enabled));
+ }
+
+ /* Mask all event channels. */
+ for (i = 0; i < nitems(s->evtchn_mask); i++)
+ atomic_store_rel_long(&s->evtchn_mask[i], ~0);
+
+ /* Remove port -> isrc mappings */
+ memset(xen_intr_port_to_isrc, 0, sizeof(xen_intr_port_to_isrc));
+
+ /* Free unused isrcs and rebind VIRQs and IPIs */
+ for (isrc_idx = 0; isrc_idx < xen_intr_isrc_count; isrc_idx++) {
+ u_int vector;
+
+ vector = FIRST_EVTCHN_INT + isrc_idx;
+ isrc = (struct xenisrc *)intr_lookup_source(vector);
+ if (isrc != NULL) {
+ isrc->xi_port = 0;
+ switch (isrc->xi_type) {
+ case EVTCHN_TYPE_IPI:
+ xen_rebind_ipi(isrc);
+ break;
+ case EVTCHN_TYPE_VIRQ:
+ xen_rebind_virq(isrc);
+ break;
+ default:
+ isrc->xi_cpu = 0;
+ break;
+ }
+ }
+ }
}
/**
@@ -693,6 +793,7 @@ xen_intr_config_intr(struct intsrc *isrc, enum intr_trigger trig,
static int
xen_intr_assign_cpu(struct intsrc *base_isrc, u_int apic_id)
{
+#ifdef SMP
struct evtchn_bind_vcpu bind_vcpu;
struct xenisrc *isrc;
u_int to_cpu, acpi_id;
@@ -749,6 +850,9 @@ xen_intr_assign_cpu(struct intsrc *base_isrc, u_int apic_id)
}
mtx_unlock(&xen_intr_isrc_lock);
return (0);
+#else
+ return (EOPNOTSUPP);
+#endif
}
/*------------------- Virtual Interrupt Source PIC Functions -----------------*/
@@ -979,8 +1083,11 @@ xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu,
error = xen_intr_bind_isrc(&isrc, bind_virq.port, EVTCHN_TYPE_VIRQ, dev,
filter, handler, arg, flags, port_handlep);
+
+#ifdef SMP
if (error == 0)
error = intr_event_bind(isrc->xi_intsrc.is_event, cpu);
+#endif
if (error != 0) {
evtchn_close_t close = { .port = bind_virq.port };
@@ -991,6 +1098,7 @@ xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu,
return (error);
}
+#ifdef SMP
if (isrc->xi_cpu != cpu) {
/*
* Too early in the boot process for the generic interrupt
@@ -1000,12 +1108,15 @@ xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu,
*/
xen_intr_assign_cpu(&isrc->xi_intsrc, cpu_apic_ids[cpu]);
}
+#endif
/*
* The Event Channel API opened this port, so it is
* responsible for closing it automatically on unbind.
*/
isrc->xi_close = 1;
+ isrc->xi_virq = virq;
+
return (0);
}
@@ -1014,6 +1125,7 @@ xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu,
driver_filter_t filter, enum intr_type flags,
xen_intr_handle_t *port_handlep)
{
+#ifdef SMP
int acpi_id = pcpu_find(cpu)->pc_acpi_id;
struct xenisrc *isrc;
struct evtchn_bind_ipi bind_ipi = { .vcpu = acpi_id };
@@ -1063,6 +1175,9 @@ xen_intr_alloc_and_bind_ipi(device_t dev, u_int cpu,
*/
isrc->xi_close = 1;
return (0);
+#else
+ return (EOPNOTSUPP);
+#endif
}
int
OpenPOWER on IntegriCloud