Changeset View
Changeset View
Standalone View
Standalone View
sys/xen/xen_intr.c
Show First 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | |||||
#include <sys/pcpu.h> | #include <sys/pcpu.h> | ||||
#include <sys/smp.h> | #include <sys/smp.h> | ||||
#include <sys/refcount.h> | #include <sys/refcount.h> | ||||
#include <vm/vm.h> | #include <vm/vm.h> | ||||
#include <vm/pmap.h> | #include <vm/pmap.h> | ||||
#include <machine/intr_machdep.h> | #include <machine/intr_machdep.h> | ||||
#include <x86/apicvar.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/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 <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 | ||||
#include <ddb/ddb.h> | #include <ddb/ddb.h> | ||||
#endif | #endif | ||||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
DPCPU_DEFINE_STATIC(struct xen_intr_pcpu_data, xen_intr_pcpu) = { | DPCPU_DEFINE_STATIC(struct xen_intr_pcpu_data, xen_intr_pcpu) = { | ||||
.last_processed_l1i = LONG_BIT - 1, | .last_processed_l1i = LONG_BIT - 1, | ||||
.last_processed_l2i = LONG_BIT - 1 | .last_processed_l2i = LONG_BIT - 1 | ||||
}; | }; | ||||
DPCPU_DECLARE(struct vcpu_info *, vcpu_info); | DPCPU_DECLARE(struct vcpu_info *, vcpu_info); | ||||
struct xenisrc { | |||||
xen_arch_isrc_t xi_arch; /* @TOP -> *xi_arch=*xenisrc */ | |||||
enum evtchn_type xi_type; | |||||
int xi_cpu; /* VCPU for delivery. */ | |||||
evtchn_port_t xi_port; | |||||
int xi_virq; | |||||
void *xi_cookie; | |||||
u_int xi_close:1; /* close on unbind? */ | |||||
u_int xi_masked:1; | |||||
volatile u_int xi_refcount; | |||||
}; | |||||
static void xen_intr_suspend(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); | |||||
static void xen_intr_enable_intr(struct intsrc *isrc); | |||||
static void xen_intr_disable_intr(struct intsrc *isrc); | |||||
static int xen_intr_vector(struct intsrc *isrc); | |||||
static int xen_intr_source_pending(struct intsrc *isrc); | |||||
static int xen_intr_config_intr(struct intsrc *isrc, | |||||
enum intr_trigger trig, enum intr_polarity pol); | |||||
static int xen_intr_assign_cpu(struct intsrc *isrc, u_int to_cpu); | |||||
/** | |||||
* PIC interface for all event channel port types except physical IRQs. | |||||
*/ | |||||
struct pic xen_intr_pic = { | |||||
.pic_enable_source = xen_intr_enable_source, | |||||
.pic_disable_source = xen_intr_disable_source, | |||||
.pic_eoi_source = xen_intr_eoi_source, | |||||
.pic_enable_intr = xen_intr_enable_intr, | |||||
.pic_disable_intr = xen_intr_disable_intr, | |||||
.pic_vector = xen_intr_vector, | |||||
.pic_source_pending = xen_intr_source_pending, | |||||
.pic_suspend = xen_intr_suspend, | |||||
.pic_resume = xen_intr_resume, | |||||
.pic_config_intr = xen_intr_config_intr, | |||||
.pic_assign_cpu = xen_intr_assign_cpu | |||||
}; | |||||
/* | /* | ||||
* Lock for interrupt core data. Notably modifying xen_intr_port_to_isrc[], | * Lock for interrupt core data. Notably modifying xen_intr_port_to_isrc[], | ||||
* or isrc->xi_port (which implies the former) requires this lock be held. | * or isrc->xi_port (which implies the former) requires this lock be held. | ||||
* | * | ||||
* Any time this lock is not held, the condition | * Any time this lock is not held, the condition | ||||
* `!xen_intr_port_to_isrc[i] || (xen_intr_port_to_isrc[i]->ix_port == i)` | * `!xen_intr_port_to_isrc[i] || (xen_intr_port_to_isrc[i]->ix_port == i)` | ||||
* MUST be true for all values of i which are valid indicies of the array. | * MUST be true for all values of i which are valid indicies of the array. | ||||
*/ | */ | ||||
▲ Show 20 Lines • Show All 116 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_arch.xai_intsrc.is_handlers == 0, | KASSERT(xen_arch_intr_has_handlers(isrc), | ||||
("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 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | |||||
* | * | ||||
* \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_arch.xai_intsrc.is_handlers == 0, | KASSERT(xen_arch_intr_has_handlers(isrc), | ||||
("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. */ | ||||
xen_intr_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); | xen_intr_cpu_mask_port(isrc->xi_cpu, isrc->xi_port); | ||||
xen_intr_cpu_unmask_port(0, isrc->xi_port); | xen_intr_cpu_unmask_port(0, isrc->xi_port); | ||||
▲ Show 20 Lines • Show All 66 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_arch.xai_intsrc, intr_next_cpu(0)); | xen_intr_assign_cpu(isrc, 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_arch.xai_intsrc, trap_frame); | xen_arch_intr_execute_handlers(isrc, 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 41 Lines • ▼ Show 20 Lines | CPU_FOREACH(i) { | ||||
pcpu = DPCPU_ID_PTR(i, xen_intr_pcpu); | pcpu = DPCPU_ID_PTR(i, xen_intr_pcpu); | ||||
memset(pcpu->evtchn_enabled, i == 0 ? ~0 : 0, | memset(pcpu->evtchn_enabled, i == 0 ? ~0 : 0, | ||||
sizeof(pcpu->evtchn_enabled)); | sizeof(pcpu->evtchn_enabled)); | ||||
} | } | ||||
for (i = 0; i < nitems(s->evtchn_mask); i++) | for (i = 0; i < nitems(s->evtchn_mask); i++) | ||||
atomic_store_rel_long(&s->evtchn_mask[i], ~0); | atomic_store_rel_long(&s->evtchn_mask[i], ~0); | ||||
intr_register_pic(&xen_intr_pic); | xen_arch_intr_init(); | ||||
if (bootverbose) | if (bootverbose) | ||||
printf("Xen interrupt system initialized\n"); | printf("Xen interrupt system initialized\n"); | ||||
return (0); | return (0); | ||||
} | } | ||||
SYSINIT(xen_intr_init, SI_SUB_INTR, SI_ORDER_SECOND, xen_intr_init, NULL); | SYSINIT(xen_intr_init, SI_SUB_INTR, SI_ORDER_SECOND, xen_intr_init, NULL); | ||||
Show All 24 Lines | xen_intr_alloc_irqs(void) | ||||
num_io_irqs += NR_EVENT_CHANNELS; | num_io_irqs += NR_EVENT_CHANNELS; | ||||
} | } | ||||
/*--------------------------- Common PIC Functions ---------------------------*/ | /*--------------------------- Common PIC Functions ---------------------------*/ | ||||
#if defined(__amd64__) || defined(__i386__) | #if defined(__amd64__) || defined(__i386__) | ||||
/** | /** | ||||
* Prepare this PIC for system suspension. | * Prepare this PIC for system suspension. | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_suspend(struct pic *unused) | xen_intr_suspend(void) | ||||
{ | { | ||||
/* Nothing to do on suspend */ | |||||
} | } | ||||
static void | static void | ||||
xen_rebind_ipi(struct xenisrc *isrc) | xen_rebind_ipi(struct xenisrc *isrc) | ||||
{ | { | ||||
#ifdef SMP | #ifdef SMP | ||||
int cpu = isrc->xi_cpu; | int cpu = isrc->xi_cpu; | ||||
int vcpu_id = PCPU_ID_GET(cpu); | int vcpu_id = PCPU_ID_GET(cpu); | ||||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | case EVTCHN_TYPE_VIRQ: | ||||
break; | break; | ||||
default: | default: | ||||
return; | return; | ||||
} | } | ||||
isrc->xi_cpu = 0; | isrc->xi_cpu = 0; | ||||
#ifdef SMP | #ifdef SMP | ||||
error = xen_intr_assign_cpu(&isrc->xi_arch.xai_intsrc, cpu); | error = xen_intr_assign_cpu(isrc, 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 | ||||
} | } | ||||
/** | /** | ||||
* Return this PIC to service after being suspended. | * Return this PIC to service after being suspended. | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_resume(struct pic *unused, bool suspend_cancelled) | xen_intr_resume(bool suspend_cancelled) | ||||
{ | { | ||||
shared_info_t *s = HYPERVISOR_shared_info; | shared_info_t *s = HYPERVISOR_shared_info; | ||||
u_int isrc_idx; | u_int isrc_idx; | ||||
int i; | int i; | ||||
if (suspend_cancelled) | if (suspend_cancelled) | ||||
return; | return; | ||||
▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
#endif | #endif | ||||
/** | /** | ||||
* Disable a Xen interrupt source. | * Disable a Xen interrupt source. | ||||
* | * | ||||
* \param isrc The interrupt source to disable. | * \param isrc The interrupt source to disable. | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_disable_intr(struct intsrc *base_isrc) | xen_intr_disable_intr(struct xenisrc *isrc) | ||||
{ | { | ||||
struct xenisrc *isrc = (struct xenisrc *)base_isrc; | |||||
evtchn_mask_port(isrc->xi_port); | evtchn_mask_port(isrc->xi_port); | ||||
} | } | ||||
/** | /** | ||||
* Determine the global interrupt vector number for | |||||
* a Xen interrupt source. | |||||
* | |||||
* \param isrc The interrupt source to query. | |||||
* | |||||
* \return The vector number corresponding to the given interrupt source. | |||||
*/ | |||||
static int | |||||
xen_intr_vector(struct intsrc *base_isrc) | |||||
{ | |||||
struct xenisrc *isrc = (struct xenisrc *)base_isrc; | |||||
return (isrc->xi_arch.xai_vector); | |||||
} | |||||
/** | |||||
* Determine whether or not interrupt events are pending on the | |||||
* the given interrupt source. | |||||
* | |||||
* \param isrc The interrupt source to query. | |||||
* | |||||
* \returns 0 if no events are pending, otherwise non-zero. | |||||
*/ | |||||
static int | |||||
xen_intr_source_pending(struct intsrc *isrc) | |||||
{ | |||||
/* | |||||
* EventChannels are edge triggered and never masked. | |||||
* There can be no pending events. | |||||
*/ | |||||
return (0); | |||||
} | |||||
/** | |||||
* Perform configuration of an interrupt source. | |||||
* | |||||
* \param isrc The interrupt source to configure. | |||||
* \param trig Edge or level. | |||||
* \param pol Active high or low. | |||||
* | |||||
* \returns 0 if no events are pending, otherwise non-zero. | |||||
*/ | |||||
static int | |||||
xen_intr_config_intr(struct intsrc *isrc, enum intr_trigger trig, | |||||
enum intr_polarity pol) | |||||
{ | |||||
/* Configuration is only possible via the evtchn apis. */ | |||||
return (ENODEV); | |||||
} | |||||
/** | |||||
* Configure CPU affinity for interrupt source event delivery. | * Configure CPU affinity for interrupt source event delivery. | ||||
* | * | ||||
* \param isrc The interrupt source to configure. | * \param isrc The interrupt source to configure. | ||||
* \param to_cpu The id of the CPU for handling future events. | * \param to_cpu The id of the CPU for handling future events. | ||||
* | * | ||||
* \returns 0 if successful, otherwise an errno. | * \returns 0 if successful, otherwise an errno. | ||||
*/ | */ | ||||
static int | int | ||||
xen_intr_assign_cpu(struct intsrc *base_isrc, u_int to_cpu) | xen_intr_assign_cpu(struct xenisrc *isrc, u_int to_cpu) | ||||
{ | { | ||||
#ifdef SMP | #ifdef SMP | ||||
struct evtchn_bind_vcpu bind_vcpu; | struct evtchn_bind_vcpu bind_vcpu; | ||||
struct xenisrc *isrc; | |||||
u_int vcpu_id = PCPU_ID_GET(to_cpu); | u_int vcpu_id = PCPU_ID_GET(to_cpu); | ||||
int error, masked; | int error, masked; | ||||
if (!xen_support_evtchn_rebind()) | if (!xen_support_evtchn_rebind()) | ||||
return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
mtx_lock(&xen_intr_isrc_lock); | mtx_lock(&xen_intr_isrc_lock); | ||||
isrc = (struct xenisrc *)base_isrc; | |||||
if (isrc->xi_port >= NR_EVENT_CHANNELS) { | if (isrc->xi_port >= NR_EVENT_CHANNELS) { | ||||
mtx_unlock(&xen_intr_isrc_lock); | mtx_unlock(&xen_intr_isrc_lock); | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
/* | /* | ||||
* Mask the event channel while binding it to prevent interrupt | * Mask the event channel while binding it to prevent interrupt | ||||
* delivery with an inconsistent state in isrc->xi_cpu. | * delivery with an inconsistent state in isrc->xi_cpu. | ||||
Show All 34 Lines | #else | ||||
return (EOPNOTSUPP); | return (EOPNOTSUPP); | ||||
#endif | #endif | ||||
} | } | ||||
/*------------------- Virtual Interrupt Source PIC Functions -----------------*/ | /*------------------- Virtual Interrupt Source PIC Functions -----------------*/ | ||||
/* | /* | ||||
* Mask a level triggered interrupt source. | * Mask a level triggered interrupt source. | ||||
* | * | ||||
* \param isrc The interrupt source to mask (if necessary). | * \param isrc The interrupt source to mask (if necessary). | ||||
* \param eoi If non-zero, perform any necessary end-of-interrupt | * \param need_eoi If true, perform any necessary end-of-interrupt | ||||
* acknowledgements. | * acknowledgements. | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_disable_source(struct intsrc *base_isrc, int eoi) | xen_intr_disable_source(struct xenisrc *isrc, bool need_eoi) | ||||
{ | { | ||||
struct xenisrc *isrc; | |||||
isrc = (struct xenisrc *)base_isrc; | |||||
/* | /* | ||||
* NB: checking if the event channel is already masked is | * NB: checking if the event channel is already masked is | ||||
* needed because the event channel user-space device | * needed because the event channel user-space device | ||||
* masks event channels on its filter as part of its | * masks event channels on its filter as part of its | ||||
* normal operation, and those shouldn't be automatically | * normal operation, and those shouldn't be automatically | ||||
* unmasked by the generic interrupt code. The event channel | * unmasked by the generic interrupt code. The event channel | ||||
* device will unmask them when needed. | * device will unmask them when needed. | ||||
*/ | */ | ||||
isrc->xi_masked = !!evtchn_test_and_set_mask(isrc->xi_port); | isrc->xi_masked = !!evtchn_test_and_set_mask(isrc->xi_port); | ||||
} | } | ||||
/* | /* | ||||
* Unmask a level triggered interrupt source. | * Unmask a level triggered interrupt source. | ||||
* | * | ||||
* \param isrc The interrupt source to unmask (if necessary). | * \param isrc The interrupt source to unmask (if necessary). | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_enable_source(struct intsrc *base_isrc) | xen_intr_enable_source(struct xenisrc *isrc) | ||||
{ | { | ||||
struct xenisrc *isrc; | |||||
isrc = (struct xenisrc *)base_isrc; | |||||
if (isrc->xi_masked == 0) | if (isrc->xi_masked == 0) | ||||
evtchn_unmask_port(isrc->xi_port); | evtchn_unmask_port(isrc->xi_port); | ||||
} | } | ||||
/* | /* | ||||
* Perform any necessary end-of-interrupt acknowledgements. | * Perform any necessary end-of-interrupt acknowledgements. | ||||
* | * | ||||
* \param isrc The interrupt source to EOI. | * \param isrc The interrupt source to EOI. | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_eoi_source(struct intsrc *base_isrc) | xen_intr_eoi_source(struct xenisrc *isrc) | ||||
{ | { | ||||
} | } | ||||
/* | /* | ||||
* Enable and unmask the interrupt source. | * Enable and unmask the interrupt source. | ||||
* | * | ||||
* \param isrc The interrupt source to enable. | * \param isrc The interrupt source to enable. | ||||
*/ | */ | ||||
static void | void | ||||
xen_intr_enable_intr(struct intsrc *base_isrc) | xen_intr_enable_intr(struct xenisrc *isrc) | ||||
{ | { | ||||
struct xenisrc *isrc = (struct xenisrc *)base_isrc; | |||||
evtchn_unmask_port(isrc->xi_port); | evtchn_unmask_port(isrc->xi_port); | ||||
} | } | ||||
/*--------------------------- 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, | ||||
▲ Show 20 Lines • Show All 112 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_arch.xai_intsrc.is_event, 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 = 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_arch.xai_intsrc, 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; | ||||
Show All 40 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_arch.xai_intsrc, cpu); | 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 All 12 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_arch.xai_vector, isrc->xi_cookie, descr)); | |||||
return (xen_arch_intr_describe(isrc, 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, | ||||
("NULL xen_intr_handle_t passed to %s", __func__)); | ("NULL xen_intr_handle_t passed to %s", __func__)); | ||||
isrc = xen_intr_isrc_from_handle(*port_handlep); | isrc = xen_intr_isrc_from_handle(*port_handlep); | ||||
*port_handlep = NULL; | *port_handlep = NULL; | ||||
if (isrc == NULL) | if (isrc == NULL) | ||||
return; | return; | ||||
mtx_lock(&xen_intr_isrc_lock); | mtx_lock(&xen_intr_isrc_lock); | ||||
if (refcount_release(&isrc->xi_refcount) == 0) { | if (refcount_release(&isrc->xi_refcount) == 0) { | ||||
mtx_unlock(&xen_intr_isrc_lock); | mtx_unlock(&xen_intr_isrc_lock); | ||||
return; | return; | ||||
} | } | ||||
mtx_unlock(&xen_intr_isrc_lock); | mtx_unlock(&xen_intr_isrc_lock); | ||||
if (isrc->xi_cookie != NULL) | if (isrc->xi_cookie != NULL) | ||||
intr_remove_handler(isrc->xi_cookie); | xen_arch_intr_remove_handler(isrc); | ||||
ehem_freebsd_m5p.com: One can argue against folding bugfixes into other commits, but this one might be appropriate. | |||||
xen_intr_release_isrc(isrc); | xen_intr_release_isrc(isrc); | ||||
} | } | ||||
void | void | ||||
xen_intr_signal(xen_intr_handle_t handle) | xen_intr_signal(xen_intr_handle_t handle) | ||||
{ | { | ||||
struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
Show All 26 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_arch.xai_vector,filter, handler, arg, | error = xen_arch_intr_add_handler(name, filter, handler, arg, | ||||
flags|INTR_EXCL, &isrc->xi_cookie, 0); | flags | INTR_EXCL, isrc); | ||||
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); | ||||
} | } | ||||
int | int | ||||
▲ Show 20 Lines • Show All 87 Lines • Show Last 20 Lines |
One can argue against folding bugfixes into other commits, but this one might be appropriate. After the call to xen_arch_intr_remove_handler() should be isrc->xi_cookie = NULL;. This is to prevent a potential double-free issue.