Changeset View
Changeset View
Standalone View
Standalone View
sys/xen/xen_intr.c
| Show First 20 Lines • Show All 44 Lines • ▼ Show 20 Lines | |||||
| #include <sys/interrupt.h> | #include <sys/interrupt.h> | ||||
| #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 <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 <machine/xen/arch-intr.h> | ||||
| #include <xen/hvm.h> | #include <xen/hvm.h> | ||||
| ▲ Show 20 Lines • Show All 52 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; | |||||
| u_int xi_cpu; /* VCPU for delivery. */ | |||||
| evtchn_port_t xi_port; | |||||
| u_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. | * Lock for interrupt core data. | ||||
| * | * | ||||
| * Modifying xen_intr_port_to_isrc[], or isrc->xi_port (implies the former) | * Modifying xen_intr_port_to_isrc[], or isrc->xi_port (implies the former) | ||||
| * requires this lock be held. Any time this lock is not held, the condition | * requires this lock be held. 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 118 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 55 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. */ | ||||
| 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 74 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 120 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#%u", | ("Received unexpected event on vCPU#%d, event bound to vCPU#%u", | ||||
| 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 All 37 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], ~0UL); | atomic_store_rel_long(&s->evtchn_mask[i], ~0UL); | ||||
| 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 23 Lines | xen_intr_alloc_irqs(void) | ||||
| first_evtchn_irq = num_io_irqs; | first_evtchn_irq = num_io_irqs; | ||||
| num_io_irqs += NR_EVENT_CHANNELS; | num_io_irqs += NR_EVENT_CHANNELS; | ||||
| } | } | ||||
| /*--------------------------- Common PIC Functions ---------------------------*/ | /*--------------------------- Common PIC Functions ---------------------------*/ | ||||
| /** | /** | ||||
| * 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 | ||||
| u_int cpu = isrc->xi_cpu; | u_int cpu = isrc->xi_cpu; | ||||
| u_int vcpu_id = PCPU_ID_GET(cpu); | u_int vcpu_id = PCPU_ID_GET(cpu); | ||||
| ▲ Show 20 Lines • Show All 55 Lines • ▼ Show 20 Lines | default: | ||||
| return (NULL); | return (NULL); | ||||
| } | } | ||||
| prev = xen_intr_port_to_isrc[isrc->xi_port]; | prev = xen_intr_port_to_isrc[isrc->xi_port]; | ||||
| xen_intr_port_to_isrc[isrc->xi_port] = isrc; | xen_intr_port_to_isrc[isrc->xi_port] = isrc; | ||||
| #ifdef SMP | #ifdef SMP | ||||
| isrc->xi_cpu = 0; | isrc->xi_cpu = 0; | ||||
| error = xen_intr_assign_cpu(&isrc->xi_arch.xai_intsrc, cpu); | error = xen_intr_assign_cpu(isrc, cpu); | ||||
| if (error) | if (error) | ||||
| panic(errfmt, __func__, cpu, error, isrc->xi_virq); | panic(errfmt, __func__, cpu, error, isrc->xi_virq); | ||||
| #endif | #endif | ||||
| evtchn_unmask_port(isrc->xi_port); | evtchn_unmask_port(isrc->xi_port); | ||||
| return (prev); | return (prev); | ||||
| } | } | ||||
| /** | /** | ||||
| * 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; | ||||
| u_int i; | u_int i; | ||||
| if (suspend_cancelled) | if (suspend_cancelled) | ||||
| return; | return; | ||||
| Show All 35 Lines | xen_intr_resume(bool suspend_cancelled) | ||||
| } | } | ||||
| } | } | ||||
| /** | /** | ||||
| * 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; | |||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| 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; | |||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| 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; | ||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| if (!xen_has_percpu_evtchn()) | if (!xen_has_percpu_evtchn()) | ||||
| 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; | |||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| 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; | |||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| 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) | ||||
| { | { | ||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| } | } | ||||
| /* | /* | ||||
| * 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; | |||||
| CTASSERT(offsetof(struct xenisrc, xi_arch.xai_intsrc) == 0); | |||||
| 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 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_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 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_arch.xai_vector, isrc->xi_cookie, descr)); | |||||
| return (xen_arch_intr_describe(isrc, 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, | ||||
| ("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, isrc->xi_cookie); | ||||
| isrc->xi_cookie = NULL; | |||||
| } | |||||
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, &isrc->xi_cookie); | ||||
| 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.