diff options
Diffstat (limited to 'sys/x86/xen/xen_intr.c')
-rw-r--r-- | sys/x86/xen/xen_intr.c | 135 |
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 |