Changeset View
Changeset View
Standalone View
Standalone View
sys/x86/xen/xen_intr.c
| Show All 39 Lines | |||||
| #include <sys/bus.h> | #include <sys/bus.h> | ||||
| #include <sys/malloc.h> | #include <sys/malloc.h> | ||||
| #include <sys/kernel.h> | #include <sys/kernel.h> | ||||
| #include <sys/limits.h> | #include <sys/limits.h> | ||||
| #include <sys/lock.h> | #include <sys/lock.h> | ||||
| #include <sys/mutex.h> | #include <sys/mutex.h> | ||||
| #include <sys/interrupt.h> | #include <sys/interrupt.h> | ||||
| #include <sys/pcpu.h> | #include <sys/pcpu.h> | ||||
| #include <sys/proc.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 <machine/smp.h> | #include <machine/smp.h> | ||||
| #include <machine/stdarg.h> | #include <machine/stdarg.h> | ||||
| #include <xen/xen-os.h> | #include <xen/xen-os.h> | ||||
| #include <xen/hvm.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 All 29 Lines | struct xen_intr_pcpu_data { | ||||
| u_int last_processed_l1i; | u_int last_processed_l1i; | ||||
| /** | /** | ||||
| * The last event channel processed within the event channel | * The last event channel processed within the event channel | ||||
| * bitmap being scanned. | * bitmap being scanned. | ||||
| */ | */ | ||||
| u_int last_processed_l2i; | u_int last_processed_l2i; | ||||
| /** Pointer to this CPU's interrupt statistic counter. */ | |||||
| u_long *evtchn_intrcnt; | |||||
| /** | /** | ||||
| * A bitmap of ports that can be serviced from this CPU. | * A bitmap of ports that can be serviced from this CPU. | ||||
| * A set bit means interrupt handling is enabled. | * A set bit means interrupt handling is enabled. | ||||
| */ | */ | ||||
| u_long evtchn_enabled[sizeof(u_long) * 8]; | u_long evtchn_enabled[sizeof(u_long) * 8]; | ||||
| }; | }; | ||||
| /* | /* | ||||
| ▲ Show 20 Lines • Show All 141 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| struct xen_intr_pcpu_data *pcpu; | struct xen_intr_pcpu_data *pcpu; | ||||
| pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu); | pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu); | ||||
| xen_set_bit(port, pcpu->evtchn_enabled); | xen_set_bit(port, pcpu->evtchn_enabled); | ||||
| } | } | ||||
| /** | /** | ||||
| * Allocate and register a per-cpu Xen upcall interrupt counter. | |||||
| * | |||||
| * \param cpu The cpu for which to register this interrupt count. | |||||
| */ | |||||
| static void | |||||
| xen_intr_intrcnt_add(u_int cpu) | |||||
| { | |||||
| char buf[MAXCOMLEN + 1]; | |||||
| struct xen_intr_pcpu_data *pcpu; | |||||
| pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu); | |||||
| if (pcpu->evtchn_intrcnt != NULL) | |||||
| return; | |||||
| snprintf(buf, sizeof(buf), "cpu%d:xen", cpu); | |||||
| intrcnt_add(buf, &pcpu->evtchn_intrcnt); | |||||
| } | |||||
| /** | |||||
| * Search for an already allocated but currently unused Xen interrupt | * Search for an already allocated but currently unused Xen interrupt | ||||
| * 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. | ||||
| */ | */ | ||||
| ▲ Show 20 Lines • Show All 229 Lines • ▼ Show 20 Lines | return (sh->evtchn_pending[idx] | ||||
| & pcpu->evtchn_enabled[idx]); | & pcpu->evtchn_enabled[idx]); | ||||
| } | } | ||||
| /** | /** | ||||
| * Interrupt handler for processing all Xen event channel events. | * Interrupt handler for processing all Xen event channel events. | ||||
| * | * | ||||
| * \param trap_frame The trap frame context for the current interrupt. | * \param trap_frame The trap frame context for the current interrupt. | ||||
| */ | */ | ||||
| void | int | ||||
| xen_intr_handle_upcall(struct trapframe *trap_frame) | xen_intr_handle_upcall(void *unused __unused) | ||||
| { | { | ||||
| struct trapframe *trap_frame = curthread->td_intr_frame; | |||||
| u_int l1i, l2i, port, cpu __diagused; | u_int l1i, l2i, port, cpu __diagused; | ||||
| u_long masked_l1, masked_l2; | u_long masked_l1, masked_l2; | ||||
| struct xenisrc *isrc; | struct xenisrc *isrc; | ||||
| vcpu_info_t *v; | vcpu_info_t *v; | ||||
| struct xen_intr_pcpu_data *pc; | struct xen_intr_pcpu_data *pc; | ||||
| u_long l1, l2; | u_long l1, l2; | ||||
| /* | /* We must remain on the same vCPU during this function */ | ||||
| * Disable preemption in order to always check and fire events | CRITICAL_ASSERT(curthread); | ||||
| * on the right vCPU | |||||
| */ | |||||
| critical_enter(); | |||||
| cpu = PCPU_GET(cpuid); | cpu = PCPU_GET(cpuid); | ||||
| pc = DPCPU_PTR(xen_intr_pcpu); | pc = DPCPU_PTR(xen_intr_pcpu); | ||||
| v = DPCPU_GET(vcpu_info); | v = DPCPU_GET(vcpu_info); | ||||
| if (!xen_has_percpu_evtchn()) { | if (!xen_has_percpu_evtchn()) { | ||||
| KASSERT((cpu == 0), ("Fired PCI event callback on wrong CPU")); | KASSERT((cpu == 0), ("Fired PCI event callback on wrong CPU")); | ||||
| } | } | ||||
| v->evtchn_upcall_pending = 0; | v->evtchn_upcall_pending = 0; | ||||
| /* No need for a barrier on x86 -- XCHG is a barrier on x86. */ | |||||
| #if 0 | #if !defined(__amd64__) && !defined(__i386__) | ||||
| #ifndef CONFIG_X86 /* No need for a barrier -- XCHG is a barrier on x86. */ | |||||
| /* Clear master flag /before/ clearing selector flag. */ | /* Clear master flag /before/ clearing selector flag. */ | ||||
| wmb(); | wmb(); | ||||
| #endif | #endif | ||||
| #endif | |||||
| l1 = atomic_readandclear_long(&v->evtchn_pending_sel); | l1 = atomic_readandclear_long(&v->evtchn_pending_sel); | ||||
| l1i = pc->last_processed_l1i; | l1i = pc->last_processed_l1i; | ||||
| l2i = pc->last_processed_l2i; | l2i = pc->last_processed_l2i; | ||||
| (*pc->evtchn_intrcnt)++; | |||||
| while (l1 != 0) { | while (l1 != 0) { | ||||
| l1i = (l1i + 1) % LONG_BIT; | l1i = (l1i + 1) % LONG_BIT; | ||||
| masked_l1 = l1 & ((~0UL) << l1i); | masked_l1 = l1 & ((~0UL) << l1i); | ||||
| if (masked_l1 == 0) { | if (masked_l1 == 0) { | ||||
| /* | /* | ||||
| * if we masked out all events, wrap around | * if we masked out all events, wrap around | ||||
| ▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | if (l2 == 0) { | ||||
| /* | /* | ||||
| * We handled all ports, so we can clear the | * We handled all ports, so we can clear the | ||||
| * selector bit. | * selector bit. | ||||
| */ | */ | ||||
| l1 &= ~(1UL << l1i); | l1 &= ~(1UL << l1i); | ||||
| } | } | ||||
| } | } | ||||
| if (xen_evtchn_needs_ack) | return (FILTER_HANDLED); | ||||
| lapic_eoi(); | |||||
| critical_exit(); | |||||
| } | } | ||||
| static int | static int | ||||
| xen_intr_init(void *dummy __unused) | xen_intr_init(void *dummy __unused) | ||||
| { | { | ||||
| shared_info_t *s = HYPERVISOR_shared_info; | shared_info_t *s = HYPERVISOR_shared_info; | ||||
| struct xen_intr_pcpu_data *pcpu; | struct xen_intr_pcpu_data *pcpu; | ||||
| int i; | int i; | ||||
| Show All 34 Lines | xen_intr_init(void *dummy __unused) | ||||
| intr_register_pic(&xen_intr_pic); | intr_register_pic(&xen_intr_pic); | ||||
| 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); | ||||
| static void | |||||
| xen_intrcnt_init(void *dummy __unused) | |||||
| { | |||||
| unsigned int i; | |||||
| if (!xen_domain()) | |||||
| return; | |||||
| /* | |||||
| * Register interrupt count manually as we aren't guaranteed to see a | |||||
| * call to xen_intr_assign_cpu() before our first interrupt. | |||||
| */ | |||||
| CPU_FOREACH(i) | |||||
| xen_intr_intrcnt_add(i); | |||||
| } | |||||
| SYSINIT(xen_intrcnt_init, SI_SUB_INTR, SI_ORDER_MIDDLE, xen_intrcnt_init, NULL); | |||||
| void | void | ||||
| xen_intr_alloc_irqs(void) | xen_intr_alloc_irqs(void) | ||||
| { | { | ||||
| if (num_io_irqs > UINT_MAX - NR_EVENT_CHANNELS) | if (num_io_irqs > UINT_MAX - NR_EVENT_CHANNELS) | ||||
| panic("IRQ allocation overflow (num_msi_irqs too high?)"); | panic("IRQ allocation overflow (num_msi_irqs too high?)"); | ||||
| first_evtchn_irq = num_io_irqs; | first_evtchn_irq = num_io_irqs; | ||||
| ▲ Show 20 Lines • Show All 733 Lines • Show Last 20 Lines | |||||