Changeset View
Standalone View
sys/xen/xen_intr.c
Show First 20 Lines • Show All 387 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
static int | static int | ||||
xen_intr_bind_isrc(struct xenisrc **isrcp, const struct xenisrc *params, | xen_intr_bind_isrc(struct xenisrc **isrcp, const struct xenisrc *params, | ||||
const char *intr_owner, driver_filter_t filter, driver_intr_t handler, | const char *intr_owner, driver_filter_t filter, driver_intr_t handler, | ||||
void *arg, enum intr_type flags, xen_intr_handle_t *port_handlep) | void *arg, enum intr_type flags, xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
int error; | int error; | ||||
#ifdef SMP | |||||
u_int cpu; | |||||
#endif | |||||
*isrcp = NULL; | *isrcp = NULL; | ||||
if (port_handlep == NULL) { | if (port_handlep == NULL) { | ||||
printf("%s: %s: Bad event handle\n", intr_owner, __func__); | printf("%s: %s: Bad event handle\n", intr_owner, __func__); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
isrc = xen_intr_alloc_isrc(params); | isrc = xen_intr_alloc_isrc(params); | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
return (ENOSPC); | return (ENOSPC); | ||||
refcount_init(&isrc->xi_refcount, 1); | refcount_init(&isrc->xi_refcount, 1); | ||||
#ifdef SMP | |||||
cpu = isrc->xi_cpu; | |||||
#endif | |||||
isrc->xi_cpu = 0; /* initialize to the likely correct value */ | isrc->xi_cpu = 0; /* initialize to the likely correct value */ | ||||
mtx_lock(&xen_intr_isrc_lock); | mtx_lock(&xen_intr_isrc_lock); | ||||
if (__predict_false(xen_intr_port_to_isrc[isrc->xi_port] != NULL)) { | if (__predict_false(xen_intr_port_to_isrc[isrc->xi_port] != NULL)) { | ||||
xen_intr_port_to_isrc[isrc->xi_port]->xi_port = ~0U; | xen_intr_port_to_isrc[isrc->xi_port]->xi_port = ~0U; | ||||
isrc->xi_cpu = xen_intr_port_to_isrc[isrc->xi_port]->xi_cpu; | isrc->xi_cpu = xen_intr_port_to_isrc[isrc->xi_port]->xi_cpu; | ||||
} | } | ||||
xen_intr_port_to_isrc[isrc->xi_port] = isrc; | xen_intr_port_to_isrc[isrc->xi_port] = isrc; | ||||
mtx_unlock(&xen_intr_isrc_lock); | mtx_unlock(&xen_intr_isrc_lock); | ||||
/* Assign the opaque handler */ | /* Assign the opaque handler */ | ||||
*port_handlep = xen_intr_handle_from_isrc(isrc); | *port_handlep = xen_intr_handle_from_isrc(isrc); | ||||
#ifdef SMP | #ifdef SMP | ||||
if (isrc->xi_type == EVTCHN_TYPE_PORT) { | if (cpu > mp_maxid) { | ||||
/* | /* | ||||
* By default all interrupts are assigned to vCPU#0 | * By default all interrupts are assigned to vCPU#0 | ||||
* unless specified otherwise, so shuffle them to balance | * unless specified otherwise, so shuffle them to balance | ||||
* the interrupt load. | * the interrupt load. | ||||
*/ | */ | ||||
xen_intr_assign_cpu(isrc, intr_next_cpu(0)); | cpu = intr_next_cpu(0); | ||||
} | } | ||||
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) { | |||||
ehem_freebsd_m5p.com: What did I learn when looking at history? Apparently not having this for PORT channels was a… | |||||
Done Inline ActionsThen read again and the commit messages don't state such. The one for 76acc41fb7c740bc49e2638529b8cc750ff281d5 is more concerned about avoiding the APICs (this retains that). Meanwhile when this was implemented at f229f35db70f9a4bfac12c49e91bd9dd56a4975e there is no reference. I'm reverting to this was simply missed during implementation for PORT channels. ehem_freebsd_m5p.com: Then read again and the commit messages don't state such.
The one for… | |||||
/* | |||||
* 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); | |||||
} | |||||
Done Inline ActionsIf D31188 and D32866 were approved quickly, D30935 would instead merely move this piece to xen_intr_alloc_isrc(). This would reduce code duplication since no architecture would need to call xen_intr_assign_cpu(). Instead each architecture would simply assign ->xi_cpu and this would take care of the assignment. This is a more advanced version of what I was referring to in my recent comment on D30935. ehem_freebsd_m5p.com: If D31188 and D32866 were approved quickly, D30935 would instead merely move **this** piece to… | |||||
#endif | #endif | ||||
if (filter == NULL && handler == NULL) { | if (filter == NULL && handler == NULL) { | ||||
/* | /* | ||||
* No filter/handler provided, leave the event channel | * No filter/handler provided, leave the event channel | ||||
* masked and without a valid handler, the caller is | * masked and without a valid handler, the caller is | ||||
* in charge of setting that up. | * in charge of setting that up. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 503 Lines • ▼ Show 20 Lines | |||||
int | int | ||||
xen_intr_bind_local_port(device_t dev, evtchn_port_t local_port, | xen_intr_bind_local_port(device_t dev, evtchn_port_t local_port, | ||||
driver_filter_t filter, driver_intr_t handler, void *arg, | driver_filter_t filter, driver_intr_t handler, void *arg, | ||||
enum intr_type flags, xen_intr_handle_t *port_handlep) | enum intr_type flags, xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
struct xenisrc params = { | struct xenisrc params = { | ||||
.xi_type = EVTCHN_TYPE_PORT, | .xi_type = EVTCHN_TYPE_PORT, | ||||
.xi_cpu = ~0U, | |||||
.xi_port = local_port, | .xi_port = local_port, | ||||
/* | /* | ||||
* The Event Channel API didn't open this port, so it is not | * The Event Channel API didn't open this port, so it is not | ||||
* responsible for closing it automatically on unbind. | * responsible for closing it automatically on unbind. | ||||
*/ | */ | ||||
.xi_close = 0, | .xi_close = 0, | ||||
}; | }; | ||||
return (xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | return (xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | ||||
filter, handler, arg, flags, port_handlep)); | filter, handler, arg, flags, port_handlep)); | ||||
} | } | ||||
int | int | ||||
xen_intr_alloc_and_bind_local_port(device_t dev, u_int remote_domain, | xen_intr_alloc_and_bind_local_port(device_t dev, u_int remote_domain, | ||||
driver_filter_t filter, driver_intr_t handler, void *arg, | driver_filter_t filter, driver_intr_t handler, void *arg, | ||||
enum intr_type flags, xen_intr_handle_t *port_handlep) | enum intr_type flags, xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
struct xenisrc params = { | struct xenisrc params = { | ||||
.xi_type = EVTCHN_TYPE_PORT, | .xi_type = EVTCHN_TYPE_PORT, | ||||
.xi_cpu = ~0U, | |||||
}; | }; | ||||
struct evtchn_alloc_unbound alloc_unbound; | struct evtchn_alloc_unbound alloc_unbound; | ||||
int error; | int error; | ||||
alloc_unbound.dom = DOMID_SELF; | alloc_unbound.dom = DOMID_SELF; | ||||
alloc_unbound.remote_dom = remote_domain; | alloc_unbound.remote_dom = remote_domain; | ||||
error = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, | error = HYPERVISOR_event_channel_op(EVTCHNOP_alloc_unbound, | ||||
&alloc_unbound); | &alloc_unbound); | ||||
Show All 22 Lines | |||||
int | int | ||||
xen_intr_bind_remote_port(device_t dev, u_int remote_domain, | xen_intr_bind_remote_port(device_t dev, u_int remote_domain, | ||||
u_int remote_port, driver_filter_t filter, driver_intr_t handler, | u_int remote_port, driver_filter_t filter, driver_intr_t handler, | ||||
void *arg, enum intr_type flags, xen_intr_handle_t *port_handlep) | void *arg, enum intr_type flags, xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
struct xenisrc params = { | struct xenisrc params = { | ||||
.xi_type = EVTCHN_TYPE_PORT, | .xi_type = EVTCHN_TYPE_PORT, | ||||
.xi_cpu = ~0U, | |||||
}; | }; | ||||
struct evtchn_bind_interdomain bind_interdomain; | struct evtchn_bind_interdomain bind_interdomain; | ||||
int error; | int error; | ||||
bind_interdomain.remote_dom = remote_domain; | bind_interdomain.remote_dom = remote_domain; | ||||
bind_interdomain.remote_port = remote_port; | bind_interdomain.remote_port = remote_port; | ||||
error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, | error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_interdomain, | ||||
&bind_interdomain); | &bind_interdomain); | ||||
Show All 27 Lines | |||||
xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu, | xen_intr_bind_virq(device_t dev, u_int virq, u_int cpu, | ||||
driver_filter_t filter, driver_intr_t handler, void *arg, | driver_filter_t filter, driver_intr_t handler, void *arg, | ||||
enum intr_type flags, xen_intr_handle_t *port_handlep) | enum intr_type flags, xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
u_int vcpu_id = PCPU_ID_GET(cpu); | u_int vcpu_id = PCPU_ID_GET(cpu); | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
struct xenisrc params = { | struct xenisrc params = { | ||||
.xi_type = EVTCHN_TYPE_VIRQ, | .xi_type = EVTCHN_TYPE_VIRQ, | ||||
.xi_cpu = cpu, | |||||
.xi_virq = virq, | .xi_virq = virq, | ||||
}; | }; | ||||
struct evtchn_bind_virq bind_virq = { .virq = virq, .vcpu = vcpu_id }; | struct evtchn_bind_virq bind_virq = { .virq = virq, .vcpu = vcpu_id }; | ||||
int error; | int error; | ||||
isrc = NULL; | isrc = NULL; | ||||
error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq); | error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_virq, &bind_virq); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* | /* | ||||
* XXX Trap Hypercall error code Linuxisms in | * XXX Trap Hypercall error code Linuxisms in | ||||
* the HYPERCALL layer. | * the HYPERCALL layer. | ||||
*/ | */ | ||||
return (-error); | return (-error); | ||||
} | } | ||||
params.xi_port = bind_virq.port; | params.xi_port = bind_virq.port; | ||||
error = xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | error = xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | ||||
filter, handler, arg, flags, port_handlep); | filter, handler, arg, flags, port_handlep); | ||||
#ifdef SMP | |||||
if (error == 0) | |||||
error = xen_arch_intr_event_bind(isrc, cpu); | |||||
#endif | |||||
if (error != 0) { | if (error != 0) { | ||||
evtchn_close_t close = { .port = params.xi_port }; | evtchn_close_t close = { .port = params.xi_port }; | ||||
xen_intr_unbind(*port_handlep); | xen_intr_unbind(*port_handlep); | ||||
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) | if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) | ||||
panic("EVTCHNOP_close failed"); | panic("EVTCHNOP_close failed"); | ||||
return (error); | return (error); | ||||
} | } | ||||
#ifdef SMP | |||||
if (isrc->xi_cpu != cpu) { | |||||
/* | /* | ||||
Done Inline ActionsNow that is a funky line attribution for a diff. ehem_freebsd_m5p.com: Now **that** is a funky line attribution for a diff. | |||||
* 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 | * The Event Channel API opened this port, so it is | ||||
* responsible for closing it automatically on unbind. | * responsible for closing it automatically on unbind. | ||||
*/ | */ | ||||
isrc->xi_close = 1; | isrc->xi_close = 1; | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
xen_intr_alloc_and_bind_ipi(u_int cpu, driver_filter_t filter, | xen_intr_alloc_and_bind_ipi(u_int cpu, driver_filter_t filter, | ||||
enum intr_type flags, xen_intr_handle_t *port_handlep) | enum intr_type flags, xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
#ifdef SMP | #ifdef SMP | ||||
u_int vcpu_id = PCPU_ID_GET(cpu); | u_int vcpu_id = PCPU_ID_GET(cpu); | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
struct xenisrc params = { | struct xenisrc params = { | ||||
.xi_type = EVTCHN_TYPE_IPI, | .xi_type = EVTCHN_TYPE_IPI, | ||||
.xi_cpu = cpu, | |||||
}; | }; | ||||
struct evtchn_bind_ipi bind_ipi = { .vcpu = vcpu_id }; | struct evtchn_bind_ipi bind_ipi = { .vcpu = vcpu_id }; | ||||
/* Same size as the one used by intr_handler->ih_name. */ | /* Same size as the one used by intr_handler->ih_name. */ | ||||
char name[MAXCOMLEN + 1]; | char name[MAXCOMLEN + 1]; | ||||
int error; | int error; | ||||
isrc = NULL; | isrc = NULL; | ||||
error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi); | error = HYPERVISOR_event_channel_op(EVTCHNOP_bind_ipi, &bind_ipi); | ||||
if (error != 0) { | if (error != 0) { | ||||
/* | /* | ||||
* XXX Trap Hypercall error code Linuxisms in | * XXX Trap Hypercall error code Linuxisms in | ||||
* the HYPERCALL layer. | * the HYPERCALL layer. | ||||
*/ | */ | ||||
return (-error); | return (-error); | ||||
} | } | ||||
params.xi_port = bind_ipi.port; | params.xi_port = bind_ipi.port; | ||||
snprintf(name, sizeof(name), "cpu%u", cpu); | snprintf(name, sizeof(name), "cpu%u", cpu); | ||||
error = xen_intr_bind_isrc(&isrc, ¶ms, name, filter, NULL, NULL, | error = xen_intr_bind_isrc(&isrc, ¶ms, name, filter, NULL, NULL, | ||||
flags, port_handlep); | flags, port_handlep); | ||||
Done Inline ActionsBefore ca7af67ac958d3749ebfc4771cecf0cd7525e6a0 there was: if (error == 0) error = intr_event_bind(isrc->xi_intsrc.is_event, cpu); Here, but that got lost. I believe this is a small commit goof, so I believe doing intr_event_bind() (or xen_arch_intr_event_bind()) before the xen_intr_assign_cpu() is the correct. Otherwise, removing the intr_event_bind()/xen_arch_intr_event_bind() call for all cases is correct. ehem_freebsd_m5p.com: Before ca7af67ac958d3749ebfc4771cecf0cd7525e6a0 there was:
```
if (error == 0)
error =… | |||||
if (error != 0) { | if (error != 0) { | ||||
evtchn_close_t close = { .port = params.xi_port }; | evtchn_close_t close = { .port = params.xi_port }; | ||||
xen_intr_unbind(*port_handlep); | xen_intr_unbind(*port_handlep); | ||||
if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) | if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close)) | ||||
panic("EVTCHNOP_close failed"); | panic("EVTCHNOP_close failed"); | ||||
return (error); | 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 | * The Event Channel API opened this port, so it is | ||||
* responsible for closing it automatically on unbind. | * responsible for closing it automatically on unbind. | ||||
*/ | */ | ||||
isrc->xi_close = 1; | isrc->xi_close = 1; | ||||
return (0); | return (0); | ||||
▲ Show 20 Lines • Show All 181 Lines • Show Last 20 Lines |
What did I learn when looking at history? Apparently not having this for PORT channels was a deliberate performance decision. That seems odd, but was apparently found worthwhile.
In such case this segment should have a if (isrc->xi_type != EVTCHN_TYPE_PORT) { added around it. This retains the advantage of doing processor assignments for all interrupts during initialization and in the same location.