Changeset View
Changeset View
Standalone View
Standalone View
sys/xen/xen_intr.c
Show First 20 Lines • Show All 53 Lines • ▼ Show 20 Lines | |||||
#include <x86/apicvar.h> | #include <x86/apicvar.h> | ||||
#include <x86/apicreg.h> | #include <x86/apicreg.h> | ||||
#include <machine/smp.h> | #include <machine/smp.h> | ||||
#include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
#include <machine/xen/synch_bitops.h> | #include <machine/xen/synch_bitops.h> | ||||
#include <xen/xen-os.h> | #include <xen/xen-os.h> | ||||
#include <machine/xen/arch-intr.h> | |||||
#include <xen/hypervisor.h> | #include <xen/hypervisor.h> | ||||
#include <xen/xen_intr.h> | #include <xen/xen_intr.h> | ||||
#include <xen/evtchn/evtchnvar.h> | #include <xen/evtchn/evtchnvar.h> | ||||
#include <dev/xen/xenpci/xenpcivar.h> | #include <dev/xen/xenpci/xenpcivar.h> | ||||
#include <dev/pci/pcivar.h> | #include <dev/pci/pcivar.h> | ||||
#ifdef DDB | #ifdef DDB | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | |||||
}; | }; | ||||
DPCPU_DECLARE(struct vcpu_info *, vcpu_info); | DPCPU_DECLARE(struct vcpu_info *, vcpu_info); | ||||
#define XEN_INVALID_EVTCHN 0 /* Invalid event channel */ | #define XEN_INVALID_EVTCHN 0 /* Invalid event channel */ | ||||
#define is_valid_evtchn(x) ((x) != XEN_INVALID_EVTCHN) | #define is_valid_evtchn(x) ((x) != XEN_INVALID_EVTCHN) | ||||
struct xenisrc { | struct xenisrc { | ||||
royger: Should this be added to an x86 header instead of here so that you don't have to move it… | |||||
struct intsrc xi_intsrc; | xen_arch_isrc_t xi_arch; /* @TOP -> *xi_arch=*xenisrc */ | ||||
Done Inline ActionsWhile there could you add a CTASSERT that &xenisrc == &xenisrc->xi_arch.xai_intsrc where the code is relying on that property? I think you can use offsetof to check the field is at offset 0 at build time. royger: While there could you add a CTASSERT that &xenisrc == &xenisrc->xi_arch.xai_intsrc where the… | |||||
enum evtchn_type xi_type; | enum evtchn_type xi_type; | ||||
Done Inline ActionsMake it unsigned int while at it. royger: Make it unsigned int while at it. | |||||
int xi_cpu; /* VCPU for delivery. */ | int xi_cpu; /* VCPU for delivery. */ | ||||
int xi_vector; /* Global isrc vector number. */ | |||||
evtchn_port_t xi_port; | evtchn_port_t xi_port; | ||||
int xi_virq; | int xi_virq; | ||||
void *xi_cookie; | void *xi_cookie; | ||||
u_int xi_close:1; /* close on unbind? */ | u_int xi_close:1; /* close on unbind? */ | ||||
u_int xi_activehi:1; | |||||
u_int xi_edgetrigger:1; | |||||
Not Done Inline ActionsCan you place the removal of those fields into a separate patch and make it part of the branch to be committed? royger: Can you place the removal of those fields into a separate patch and make it part of the branch… | |||||
u_int xi_masked:1; | u_int xi_masked:1; | ||||
volatile u_int xi_refcount; | volatile u_int xi_refcount; | ||||
}; | }; | ||||
static void xen_intr_suspend(struct pic *); | static void xen_intr_suspend(struct pic *); | ||||
static void xen_intr_resume(struct pic *, bool suspend_cancelled); | static void xen_intr_resume(struct pic *, bool suspend_cancelled); | ||||
static void xen_intr_enable_source(struct intsrc *isrc); | static void xen_intr_enable_source(struct intsrc *isrc); | ||||
static void xen_intr_disable_source(struct intsrc *isrc, int eoi); | static void xen_intr_disable_source(struct intsrc *isrc, int eoi); | ||||
▲ Show 20 Lines • Show All 142 Lines • ▼ Show 20 Lines | xen_intr_find_unused_isrc(enum evtchn_type type) | ||||
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(isrc->xi_intsrc.is_handlers == 0, | KASSERT(isrc->xi_arch.xai_intsrc.is_handlers == 0, | ||||
("Free evtchn still has handlers")); | ("Free evtchn still has handlers")); | ||||
isrc->xi_type = type; | isrc->xi_type = type; | ||||
return (isrc); | return (isrc); | ||||
} | } | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
Show All 25 Lines | xen_intr_alloc_isrc(enum evtchn_type type) | ||||
vector = first_evtchn_irq + xen_intr_auto_vector_count; | vector = first_evtchn_irq + xen_intr_auto_vector_count; | ||||
xen_intr_auto_vector_count++; | xen_intr_auto_vector_count++; | ||||
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_isrc_lock); | mtx_unlock(&xen_intr_isrc_lock); | ||||
isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO); | isrc = malloc(sizeof(*isrc), M_XENINTR, M_WAITOK | M_ZERO); | ||||
isrc->xi_intsrc.is_pic = &xen_intr_pic; | isrc->xi_arch.xai_intsrc.is_pic = &xen_intr_pic; | ||||
isrc->xi_vector = vector; | isrc->xi_arch.xai_vector = vector; | ||||
isrc->xi_type = type; | isrc->xi_type = type; | ||||
intr_register_source(&isrc->xi_intsrc); | intr_register_source(&isrc->xi_arch.xai_intsrc); | ||||
mtx_lock(&xen_intr_isrc_lock); | mtx_lock(&xen_intr_isrc_lock); | ||||
return (isrc); | return (isrc); | ||||
} | } | ||||
/** | /** | ||||
* 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. | ||||
*/ | */ | ||||
static int | static int | ||||
xen_intr_release_isrc(struct xenisrc *isrc) | xen_intr_release_isrc(struct xenisrc *isrc) | ||||
{ | { | ||||
mtx_lock(&xen_intr_isrc_lock); | mtx_lock(&xen_intr_isrc_lock); | ||||
KASSERT(isrc->xi_intsrc.is_handlers == 0, | KASSERT(isrc->xi_arch.xai_intsrc.is_handlers == 0, | ||||
("Release called, but xenisrc still in use")); | ("Release called, but xenisrc still in use")); | ||||
evtchn_mask_port(isrc->xi_port); | evtchn_mask_port(isrc->xi_port); | ||||
evtchn_clear_port(isrc->xi_port); | evtchn_clear_port(isrc->xi_port); | ||||
/* Rebind port to CPU 0. */ | /* Rebind port to CPU 0. */ | ||||
evtchn_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); | evtchn_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); | ||||
evtchn_cpu_unmask_port(0, isrc->xi_port); | evtchn_cpu_unmask_port(0, isrc->xi_port); | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
#ifdef SMP | #ifdef SMP | ||||
if (type == EVTCHN_TYPE_PORT) { | if (type == EVTCHN_TYPE_PORT) { | ||||
/* | /* | ||||
* 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->xi_intsrc, intr_next_cpu(0)); | xen_intr_assign_cpu(&isrc->xi_arch.xai_intsrc, intr_next_cpu(0)); | ||||
} | } | ||||
#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 122 Lines • ▼ Show 20 Lines | do { | ||||
if (__predict_false(isrc == NULL)) | if (__predict_false(isrc == NULL)) | ||||
continue; | continue; | ||||
/* Make sure we are firing on the right vCPU */ | /* Make sure we are firing on the right vCPU */ | ||||
KASSERT((isrc->xi_cpu == PCPU_GET(cpuid)), | KASSERT((isrc->xi_cpu == PCPU_GET(cpuid)), | ||||
("Received unexpected event on vCPU#%d, event bound to vCPU#%d", | ("Received unexpected event on vCPU#%d, event bound to vCPU#%d", | ||||
PCPU_GET(cpuid), isrc->xi_cpu)); | PCPU_GET(cpuid), isrc->xi_cpu)); | ||||
intr_execute_handlers(&isrc->xi_intsrc, trap_frame); | intr_execute_handlers(&isrc->xi_arch.xai_intsrc, trap_frame); | ||||
/* | /* | ||||
* If this is the final port processed, | * If this is the final port processed, | ||||
* we'll pick up here+1 next time. | * we'll pick up here+1 next time. | ||||
*/ | */ | ||||
pc->last_processed_l1i = l1i; | pc->last_processed_l1i = l1i; | ||||
pc->last_processed_l2i = l2i; | pc->last_processed_l2i = l2i; | ||||
▲ Show 20 Lines • Show All 140 Lines • ▼ Show 20 Lines | case EVTCHN_TYPE_VIRQ: | ||||
xen_rebind_virq(isrc); | xen_rebind_virq(isrc); | ||||
break; | break; | ||||
default: | default: | ||||
return; | return; | ||||
} | } | ||||
#ifdef SMP | #ifdef SMP | ||||
isrc->xi_cpu = 0; | isrc->xi_cpu = 0; | ||||
error = xen_intr_assign_cpu(&isrc->xi_intsrc, | error = xen_intr_assign_cpu(&isrc->xi_arch.xai_intsrc, | ||||
cpu_apic_ids[cpu]); | cpu_apic_ids[cpu]); | ||||
if (error) | if (error) | ||||
panic("unable to bind xen intr#%d to CPU#%d: %d", | panic("unable to bind xen intr#%d to CPU#%d: %d", | ||||
isrc->xi_virq, cpu, error); | isrc->xi_virq, cpu, error); | ||||
#endif | #endif | ||||
} | } | ||||
/** | /** | ||||
▲ Show 20 Lines • Show All 81 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* \return The vector number corresponding to the given interrupt source. | * \return The vector number corresponding to the given interrupt source. | ||||
*/ | */ | ||||
static int | static int | ||||
xen_intr_vector(struct intsrc *base_isrc) | xen_intr_vector(struct intsrc *base_isrc) | ||||
{ | { | ||||
struct xenisrc *isrc = (struct xenisrc *)base_isrc; | struct xenisrc *isrc = (struct xenisrc *)base_isrc; | ||||
return (isrc->xi_vector); | return (isrc->xi_arch.xai_vector); | ||||
} | } | ||||
/** | /** | ||||
* Determine whether or not interrupt events are pending on the | * Determine whether or not interrupt events are pending on the | ||||
* the given interrupt source. | * the given interrupt source. | ||||
* | * | ||||
* \param isrc The interrupt source to query. | * \param isrc The interrupt source to query. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 281 Lines • ▼ Show 20 Lines | 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, | error = xen_intr_bind_isrc(&isrc, bind_virq.port, EVTCHN_TYPE_VIRQ, | ||||
device_get_nameunit(dev), filter, handler, arg, flags, | device_get_nameunit(dev), filter, handler, arg, flags, | ||||
port_handlep); | port_handlep); | ||||
#ifdef SMP | #ifdef SMP | ||||
if (error == 0) | if (error == 0) | ||||
error = intr_event_bind(isrc->xi_intsrc.is_event, cpu); | error = intr_event_bind(isrc->xi_arch.xai_intsrc.is_event, cpu); | ||||
#endif | #endif | ||||
if (error != 0) { | if (error != 0) { | ||||
evtchn_close_t close = { .port = bind_virq.port }; | evtchn_close_t close = { .port = bind_virq.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->xi_intsrc, cpu_apic_ids[cpu]); | xen_intr_assign_cpu(&isrc->xi_arch.xai_intsrc, cpu_apic_ids[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; | ||||
Show All 39 Lines | #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->xi_intsrc, cpu_apic_ids[cpu]); | xen_intr_assign_cpu(&isrc->xi_arch.xai_intsrc, cpu_apic_ids[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 All 11 Lines | xen_intr_describe(xen_intr_handle_t port_handle, const char *fmt, ...) | ||||
isrc = xen_intr_isrc_from_handle(port_handle); | isrc = xen_intr_isrc_from_handle(port_handle); | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
va_start(ap, fmt); | va_start(ap, fmt); | ||||
vsnprintf(descr, sizeof(descr), fmt, ap); | vsnprintf(descr, sizeof(descr), fmt, ap); | ||||
va_end(ap); | va_end(ap); | ||||
return (intr_describe(isrc->xi_vector, isrc->xi_cookie, descr)); | return (intr_describe(isrc->xi_arch.xai_vector, isrc->xi_cookie, descr)); | ||||
} | } | ||||
void | void | ||||
xen_intr_unbind(xen_intr_handle_t *port_handlep) | xen_intr_unbind(xen_intr_handle_t *port_handlep) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
KASSERT(port_handlep != NULL, | KASSERT(port_handlep != NULL, | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | |||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
int error; | int error; | ||||
isrc = xen_intr_isrc_from_handle(handle); | isrc = xen_intr_isrc_from_handle(handle); | ||||
if (isrc == NULL || isrc->xi_cookie != NULL) | if (isrc == NULL || isrc->xi_cookie != NULL) | ||||
return (EINVAL); | return (EINVAL); | ||||
error = intr_add_handler(name, isrc->xi_vector,filter, handler, arg, | error = intr_add_handler(name, isrc->xi_arch.xai_vector,filter, handler, arg, | ||||
flags|INTR_EXCL, &isrc->xi_cookie, 0); | flags|INTR_EXCL, &isrc->xi_cookie, 0); | ||||
if (error != 0) | if (error != 0) | ||||
printf("%s: %s: add handler failed: %d\n", name, __func__, | printf("%s: %s: add handler failed: %d\n", name, __func__, | ||||
error); | error); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 87 Lines • Show Last 20 Lines |
Should this be added to an x86 header instead of here so that you don't have to move it afterwards?
I expect you will end up moving it in a later commit?