diff --git a/sys/dev/xen/bus/xen_intr.c b/sys/dev/xen/bus/xen_intr.c --- a/sys/dev/xen/bus/xen_intr.c +++ b/sys/dev/xen/bus/xen_intr.c @@ -270,6 +270,10 @@ xen_intr_bind_isrc(struct xenisrc *isrc, const char *intr_owner, xen_intr_handle_t *const port_handlep) { +#ifdef SMP + int error; + u_int cpu; +#endif KASSERT(port_handlep != NULL, ("%s: %s: Bad event handle\n", intr_owner, __func__)); @@ -288,18 +292,43 @@ KASSERT(isrc->xi_cookie == NULL, ("%s: Called with uninitialized cookie", __func__)); +#ifdef SMP + cpu = isrc->xi_cpu; + isrc->xi_cpu = 0; /* initialize what is initially needed */ +#endif + mtx_lock(&xen_intr_isrc_lock); xen_intr_port_to_isrc[isrc->xi_port] = isrc; mtx_unlock(&xen_intr_isrc_lock); #ifdef SMP - if (isrc->xi_type == EVTCHN_TYPE_PORT) { + if (cpu > mp_maxid) { /* * By default all interrupts are assigned to vCPU#0 * unless specified otherwise, so shuffle them to balance * the interrupt load. */ - xen_intr_assign_cpu(isrc, xen_arch_intr_next_cpu(isrc)); + cpu = xen_arch_intr_next_cpu(isrc); + } + + /* IPI interrupts are bound before the APs are started and CPU_ABSENT() + * will return false. Hence intr_event_bind() would return EINVAL. */ + if (isrc->xi_type != EVTCHN_TYPE_IPI) { + error = xen_arch_intr_event_bind(isrc, cpu); + if (error != 0) { /* *very* unlikely */ + xen_intr_release_isrc(isrc); + return (error); + } + } + + if (isrc->xi_cpu != cpu) { + /* + * Too early in the boot process for the generic interrupt + * code to perform the binding. Update our event channel + * masks manually so events can't fire on the wrong cpu + * during AP startup. + */ + xen_intr_assign_cpu(isrc, cpu); } #endif @@ -861,6 +890,7 @@ return (ENOSPC); isrc->xi_type = EVTCHN_TYPE_VIRQ; + isrc->xi_cpu = cpu; isrc->xi_virq = virq; error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq); @@ -876,32 +906,14 @@ isrc->xi_port = bind_virq.port; error = xen_intr_bind_isrc(isrc, name, port_handlep); -#ifdef SMP - if (error == 0) - error = xen_arch_intr_event_bind(isrc, cpu); -#endif - if (error != 0) { evtchn_close_t close = { .port = bind_virq.port }; - xen_intr_unbind(*port_handlep); if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) panic("EVTCHNOP_close failed"); return (error); } -#ifdef SMP - if (isrc->xi_cpu != cpu) { - /* - * Too early in the boot process for the generic interrupt - * code to perform the binding. Update our event channel - * masks manually so events can't fire on the wrong cpu - * during AP startup. - */ - xen_intr_assign_cpu(isrc, cpu); - } -#endif - /* * The Event Channel API opened this port, so it is * responsible for closing it automatically on unbind. @@ -931,6 +943,7 @@ return (ENOSPC); isrc->xi_type = EVTCHN_TYPE_IPI; + isrc->xi_cpu = cpu; error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi); if (error != 0) { @@ -947,22 +960,11 @@ if (error != 0) { evtchn_close_t close = { .port = bind_ipi.port }; - xen_intr_unbind(*port_handlep); if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) panic("EVTCHNOP_close failed"); return (error); } - if (isrc->xi_cpu != cpu) { - /* - * Too early in the boot process for the generic interrupt - * code to perform the binding. Update our event channel - * masks manually so events can't fire on the wrong cpu - * during AP startup. - */ - xen_intr_assign_cpu(isrc, cpu); - } - /* * The Event Channel API opened this port, so it is * responsible for closing it automatically on unbind.