Changeset View
Changeset View
Standalone View
Standalone View
sys/xen/xen_intr.c
Show First 20 Lines • Show All 230 Lines • ▼ Show 20 Lines | |||||
* source object. | * source object. | ||||
* | * | ||||
* \param type Restrict the search to interrupt sources of the given | * \param type Restrict the search to interrupt sources of the given | ||||
* type. | * type. | ||||
* | * | ||||
* \return A pointer to a free Xen interrupt source object or NULL. | * \return A pointer to a free Xen interrupt source object or NULL. | ||||
*/ | */ | ||||
static struct xenisrc * | static struct xenisrc * | ||||
xen_intr_find_unused_isrc(enum evtchn_type type) | xen_intr_find_unused_isrc(const struct xenisrc *const params) | ||||
{ | { | ||||
u_int isrc_idx; | u_int isrc_idx; | ||||
mtx_assert(&xen_intr_x86_lock, MA_OWNED); | mtx_assert(&xen_intr_x86_lock, MA_OWNED); | ||||
for (isrc_idx = 0; isrc_idx < xen_intr_auto_vector_count; isrc_idx ++) { | for (isrc_idx = 0; isrc_idx < xen_intr_auto_vector_count; isrc_idx ++) { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
u_int vector; | u_int vector; | ||||
vector = first_evtchn_irq + isrc_idx; | vector = first_evtchn_irq + isrc_idx; | ||||
isrc = (struct xenisrc *)intr_lookup_source(vector); | isrc = (struct xenisrc *)intr_lookup_source(vector); | ||||
if (isrc != NULL | if (isrc != NULL | ||||
&& isrc->xi_type == EVTCHN_TYPE_UNBOUND) { | && isrc->xi_type == EVTCHN_TYPE_UNBOUND) { | ||||
KASSERT(!xen_arch_intr_has_handlers(isrc), | KASSERT(!xen_arch_intr_has_handlers(isrc), | ||||
("Free evtchn still has handlers")); | ("Free evtchn still has handlers")); | ||||
isrc->xi_type = type; | CTASSERT(offsetof(struct xenisrc, xi_arch) == 0); | ||||
memcpy(&isrc->xi_arch + 1, ¶ms->xi_arch + 1, | |||||
sizeof(struct xenisrc) - sizeof(xen_arch_isrc_t)); | |||||
return (isrc); | return (isrc); | ||||
} | } | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
/** | /** | ||||
* Allocate a Xen interrupt source object. | * Allocate a Xen interrupt source object. | ||||
* | * | ||||
* \param type The type of interrupt source to create. | * \param type The type of interrupt source to create. | ||||
* | * | ||||
* \return A pointer to a newly allocated Xen interrupt source | * \return A pointer to a newly allocated Xen interrupt source | ||||
* object or NULL. | * object or NULL. | ||||
*/ | */ | ||||
static struct xenisrc * | static struct xenisrc * | ||||
xen_intr_alloc_isrc(enum evtchn_type type) | xen_intr_alloc_isrc(const struct xenisrc *const params) | ||||
{ | { | ||||
static int warned; | static int warned; | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
unsigned int vector; | unsigned int vector; | ||||
mtx_lock(&xen_intr_x86_lock); | mtx_lock(&xen_intr_x86_lock); | ||||
isrc = xen_intr_find_unused_isrc(type); | isrc = xen_intr_find_unused_isrc(params); | ||||
if (isrc != NULL) { | if (isrc != NULL) { | ||||
mtx_unlock(&xen_intr_x86_lock); | mtx_unlock(&xen_intr_x86_lock); | ||||
return (isrc); | return (isrc); | ||||
} | } | ||||
if (xen_intr_auto_vector_count >= NR_EVENT_CHANNELS) { | if (xen_intr_auto_vector_count >= NR_EVENT_CHANNELS) { | ||||
if (!warned) { | if (!warned) { | ||||
Show All 9 Lines | xen_intr_alloc_isrc(const struct xenisrc *const params) | ||||
KASSERT((intr_lookup_source(vector) == NULL), | KASSERT((intr_lookup_source(vector) == NULL), | ||||
("Trying to use an already allocated vector")); | ("Trying to use an already allocated vector")); | ||||
mtx_unlock(&xen_intr_x86_lock); | mtx_unlock(&xen_intr_x86_lock); | ||||
isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO); | isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO); | ||||
isrc->xi_arch.xai_intsrc.is_pic = &xen_intr_pic; | isrc->xi_arch.xai_intsrc.is_pic = &xen_intr_pic; | ||||
isrc->xi_arch.xai_vector = vector; | isrc->xi_arch.xai_vector = vector; | ||||
isrc->xi_type = type; | |||||
CTASSERT(offsetof(struct xenisrc, xi_arch) == 0); | |||||
memcpy(&isrc->xi_arch + 1, ¶ms->xi_arch + 1, | |||||
sizeof(struct xenisrc) - sizeof(xen_arch_isrc_t)); | |||||
if (intr_register_source(&isrc->xi_arch.xai_intsrc) != 0) { | if (intr_register_source(&isrc->xi_arch.xai_intsrc) != 0) { | ||||
free(isrc, M_XENINTR); | free(isrc, M_XENINTR); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
return (isrc); | return (isrc); | ||||
} | } | ||||
ehem_freebsd_m5p.com: Clang gave an "expected expression" error at the _Static_assert() without the semicolon. This… | |||||
/** | /** | ||||
* Attempt to free an active Xen interrupt source object. | * Attempt to free an active Xen interrupt source object. | ||||
* | * | ||||
* \param isrc The interrupt source object to release. | * \param isrc The interrupt source object to release. | ||||
* | * | ||||
* \returns EBUSY if the source is still in use, otherwise 0. | * \returns EBUSY if the source is still in use, otherwise 0. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | |||||
* \param arg Argument to present to both filter and handler. | * \param arg Argument to present to both filter and handler. | ||||
* \param irqflags Interrupt handler flags. See sys/bus.h. | * \param irqflags Interrupt handler flags. See sys/bus.h. | ||||
* \param handlep Pointer to an opaque handle used to manage this | * \param handlep Pointer to an opaque handle used to manage this | ||||
* registration. | * registration. | ||||
* | * | ||||
* \returns 0 on success, otherwise an errno. | * \returns 0 on success, otherwise an errno. | ||||
*/ | */ | ||||
static int | static int | ||||
xen_intr_bind_isrc(struct xenisrc **isrcp, evtchn_port_t local_port, | xen_intr_bind_isrc(struct xenisrc **isrcp, const struct xenisrc *params, | ||||
enum evtchn_type type, const char *intr_owner, driver_filter_t filter, | const char *intr_owner, driver_filter_t filter, | ||||
driver_intr_t handler, void *arg, enum intr_type flags, | driver_intr_t handler, void *arg, enum intr_type flags, | ||||
xen_intr_handle_t *port_handlep) | xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
int error; | int error; | ||||
*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(type); | isrc = xen_intr_alloc_isrc(params); | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
return (ENOSPC); | return (ENOSPC); | ||||
mtx_lock(&xen_intr_isrc_lock); | mtx_lock(&xen_intr_isrc_lock); | ||||
isrc->xi_port = local_port; | |||||
xen_intr_port_to_isrc[isrc->xi_port] = isrc; | xen_intr_port_to_isrc[isrc->xi_port] = isrc; | ||||
refcount_init(&isrc->xi_refcount, 1); | refcount_init(&isrc->xi_refcount, 1); | ||||
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 (type == EVTCHN_TYPE_PORT) { | if (params->xi_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)); | xen_intr_assign_cpu(isrc, intr_next_cpu(0)); | ||||
} | } | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 499 Lines • ▼ Show 20 Lines | |||||
/*--------------------------- Public Functions -------------------------------*/ | /*--------------------------- Public Functions -------------------------------*/ | ||||
/*------- API comments for these methods can be found in xen/xenintr.h -------*/ | /*------- API comments for these methods can be found in xen/xenintr.h -------*/ | ||||
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; | ||||
int error; | struct xenisrc params = { | ||||
.xi_type = EVTCHN_TYPE_PORT, | |||||
error = xen_intr_bind_isrc(&isrc, local_port, EVTCHN_TYPE_PORT, | .xi_cpu = ~0U, | ||||
device_get_nameunit(dev), filter, handler, arg, flags, | .xi_port = local_port, | ||||
port_handlep); | |||||
if (error != 0) | |||||
return (error); | |||||
/* | /* | ||||
* 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. | ||||
*/ | */ | ||||
isrc->xi_close = 0; | .xi_close = 0, | ||||
return (0); | }; | ||||
return (xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | |||||
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 = { .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); | ||||
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); | ||||
} | } | ||||
error = xen_intr_bind_isrc(&isrc, alloc_unbound.port, EVTCHN_TYPE_PORT, | params.xi_port = alloc_unbound.port; | ||||
device_get_nameunit(dev), filter, handler, arg, flags, | error = xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | ||||
port_handlep); | filter, handler, arg, flags, port_handlep); | ||||
if (error != 0) { | if (error != 0) { | ||||
evtchn_close_t close = { .port = alloc_unbound.port }; | evtchn_close_t close = { .port = params.xi_port }; | ||||
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); | ||||
} | } | ||||
isrc->xi_close = 1; | isrc->xi_close = 1; | ||||
return (0); | return (0); | ||||
} | } | ||||
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 = { | |||||
.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); | ||||
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); | ||||
} | } | ||||
error = xen_intr_bind_isrc(&isrc, bind_interdomain.local_port, | params.xi_port = bind_interdomain.local_port; | ||||
EVTCHN_TYPE_PORT, device_get_nameunit(dev), filter, handler, arg, | error = xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | ||||
flags, port_handlep); | filter, handler, arg, flags, port_handlep); | ||||
if (error) { | if (error) { | ||||
evtchn_close_t close = { .port = bind_interdomain.local_port }; | evtchn_close_t close = { .port = params.xi_port }; | ||||
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); | ||||
} | } | ||||
/* | /* | ||||
* 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_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 = { | |||||
.xi_type = EVTCHN_TYPE_VIRQ, | |||||
.xi_cpu = cpu, | |||||
.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); | ||||
} | } | ||||
error = xen_intr_bind_isrc(&isrc, bind_virq.port, EVTCHN_TYPE_VIRQ, | params.xi_port = bind_virq.port; | ||||
device_get_nameunit(dev), filter, handler, arg, flags, | error = xen_intr_bind_isrc(&isrc, ¶ms, device_get_nameunit(dev), | ||||
port_handlep); | filter, handler, arg, flags, port_handlep); | ||||
#ifdef SMP | #ifdef SMP | ||||
if (error == 0) | if (error == 0) | ||||
error = xen_arch_intr_event_bind(isrc, cpu); | error = xen_arch_intr_event_bind(isrc, cpu); | ||||
#endif | #endif | ||||
if (error != 0) { | if (error != 0) { | ||||
evtchn_close_t close = { .port = bind_virq.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 | #ifdef SMP | ||||
if (isrc->xi_cpu != cpu) { | if (isrc->xi_cpu != cpu) { | ||||
/* | /* | ||||
* Too early in the boot process for the generic interrupt | * Too early in the boot process for the generic interrupt | ||||
* code to perform the binding. Update our event channel | * code to perform the binding. Update our event channel | ||||
* masks manually so events can't fire on the wrong cpu | * masks manually so events can't fire on the wrong cpu | ||||
* during AP startup. | * during AP startup. | ||||
*/ | */ | ||||
xen_intr_assign_cpu(isrc, cpu); | xen_intr_assign_cpu(isrc, cpu); | ||||
} | } | ||||
#endif | #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; | ||||
isrc->xi_virq = virq; | |||||
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 = { | |||||
.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; | |||||
snprintf(name, sizeof(name), "cpu%u", cpu); | snprintf(name, sizeof(name), "cpu%u", cpu); | ||||
error = xen_intr_bind_isrc(&isrc, bind_ipi.port, EVTCHN_TYPE_IPI, | error = xen_intr_bind_isrc(&isrc, ¶ms, name, filter, NULL, NULL, | ||||
name, filter, NULL, NULL, flags, port_handlep); | flags, port_handlep); | ||||
if (error != 0) { | if (error != 0) { | ||||
evtchn_close_t close = { .port = bind_ipi.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) { | if (isrc->xi_cpu != cpu) { | ||||
▲ Show 20 Lines • Show All 199 Lines • Show Last 20 Lines |
Clang gave an "expected expression" error at the _Static_assert() without the semicolon. This seems like a compiler bug, as it doesn't complain with the exact same CTASSERT() directly after a bracket. If someone has a better explanation I'd like to learn of it.