diff options
Diffstat (limited to 'drivers/xen/events.c')
-rw-r--r-- | drivers/xen/events.c | 95 |
1 files changed, 84 insertions, 11 deletions
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index db8f506817f0..5e1f34892dcc 100644 --- a/drivers/xen/events.c +++ b/drivers/xen/events.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/bootmem.h> | 29 | #include <linux/bootmem.h> |
30 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
31 | 31 | ||
32 | #include <asm/desc.h> | ||
32 | #include <asm/ptrace.h> | 33 | #include <asm/ptrace.h> |
33 | #include <asm/irq.h> | 34 | #include <asm/irq.h> |
34 | #include <asm/idle.h> | 35 | #include <asm/idle.h> |
@@ -36,10 +37,14 @@ | |||
36 | #include <asm/xen/hypercall.h> | 37 | #include <asm/xen/hypercall.h> |
37 | #include <asm/xen/hypervisor.h> | 38 | #include <asm/xen/hypervisor.h> |
38 | 39 | ||
40 | #include <xen/xen.h> | ||
41 | #include <xen/hvm.h> | ||
39 | #include <xen/xen-ops.h> | 42 | #include <xen/xen-ops.h> |
40 | #include <xen/events.h> | 43 | #include <xen/events.h> |
41 | #include <xen/interface/xen.h> | 44 | #include <xen/interface/xen.h> |
42 | #include <xen/interface/event_channel.h> | 45 | #include <xen/interface/event_channel.h> |
46 | #include <xen/interface/hvm/hvm_op.h> | ||
47 | #include <xen/interface/hvm/params.h> | ||
43 | 48 | ||
44 | /* | 49 | /* |
45 | * This lock protects updates to the following mapping and reference-count | 50 | * This lock protects updates to the following mapping and reference-count |
@@ -335,9 +340,18 @@ static int find_unbound_irq(void) | |||
335 | int irq; | 340 | int irq; |
336 | struct irq_desc *desc; | 341 | struct irq_desc *desc; |
337 | 342 | ||
338 | for (irq = 0; irq < nr_irqs; irq++) | 343 | for (irq = 0; irq < nr_irqs; irq++) { |
344 | desc = irq_to_desc(irq); | ||
345 | /* only 0->15 have init'd desc; handle irq > 16 */ | ||
346 | if (desc == NULL) | ||
347 | break; | ||
348 | if (desc->chip == &no_irq_chip) | ||
349 | break; | ||
350 | if (desc->chip != &xen_dynamic_chip) | ||
351 | continue; | ||
339 | if (irq_info[irq].type == IRQT_UNBOUND) | 352 | if (irq_info[irq].type == IRQT_UNBOUND) |
340 | break; | 353 | break; |
354 | } | ||
341 | 355 | ||
342 | if (irq == nr_irqs) | 356 | if (irq == nr_irqs) |
343 | panic("No available IRQ to bind to: increase nr_irqs!\n"); | 357 | panic("No available IRQ to bind to: increase nr_irqs!\n"); |
@@ -346,7 +360,7 @@ static int find_unbound_irq(void) | |||
346 | if (WARN_ON(desc == NULL)) | 360 | if (WARN_ON(desc == NULL)) |
347 | return -1; | 361 | return -1; |
348 | 362 | ||
349 | dynamic_irq_init(irq); | 363 | dynamic_irq_init_keep_chip_data(irq); |
350 | 364 | ||
351 | return irq; | 365 | return irq; |
352 | } | 366 | } |
@@ -617,17 +631,13 @@ static DEFINE_PER_CPU(unsigned, xed_nesting_count); | |||
617 | * a bitset of words which contain pending event bits. The second | 631 | * a bitset of words which contain pending event bits. The second |
618 | * level is a bitset of pending events themselves. | 632 | * level is a bitset of pending events themselves. |
619 | */ | 633 | */ |
620 | void xen_evtchn_do_upcall(struct pt_regs *regs) | 634 | static void __xen_evtchn_do_upcall(void) |
621 | { | 635 | { |
622 | int cpu = get_cpu(); | 636 | int cpu = get_cpu(); |
623 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
624 | struct shared_info *s = HYPERVISOR_shared_info; | 637 | struct shared_info *s = HYPERVISOR_shared_info; |
625 | struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); | 638 | struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); |
626 | unsigned count; | 639 | unsigned count; |
627 | 640 | ||
628 | exit_idle(); | ||
629 | irq_enter(); | ||
630 | |||
631 | do { | 641 | do { |
632 | unsigned long pending_words; | 642 | unsigned long pending_words; |
633 | 643 | ||
@@ -664,14 +674,31 @@ void xen_evtchn_do_upcall(struct pt_regs *regs) | |||
664 | 674 | ||
665 | count = __get_cpu_var(xed_nesting_count); | 675 | count = __get_cpu_var(xed_nesting_count); |
666 | __get_cpu_var(xed_nesting_count) = 0; | 676 | __get_cpu_var(xed_nesting_count) = 0; |
667 | } while(count != 1); | 677 | } while (count != 1 || vcpu_info->evtchn_upcall_pending); |
668 | 678 | ||
669 | out: | 679 | out: |
680 | |||
681 | put_cpu(); | ||
682 | } | ||
683 | |||
684 | void xen_evtchn_do_upcall(struct pt_regs *regs) | ||
685 | { | ||
686 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
687 | |||
688 | exit_idle(); | ||
689 | irq_enter(); | ||
690 | |||
691 | __xen_evtchn_do_upcall(); | ||
692 | |||
670 | irq_exit(); | 693 | irq_exit(); |
671 | set_irq_regs(old_regs); | 694 | set_irq_regs(old_regs); |
695 | } | ||
672 | 696 | ||
673 | put_cpu(); | 697 | void xen_hvm_evtchn_do_upcall(void) |
698 | { | ||
699 | __xen_evtchn_do_upcall(); | ||
674 | } | 700 | } |
701 | EXPORT_SYMBOL_GPL(xen_hvm_evtchn_do_upcall); | ||
675 | 702 | ||
676 | /* Rebind a new event channel to an existing irq. */ | 703 | /* Rebind a new event channel to an existing irq. */ |
677 | void rebind_evtchn_irq(int evtchn, int irq) | 704 | void rebind_evtchn_irq(int evtchn, int irq) |
@@ -708,7 +735,10 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu) | |||
708 | struct evtchn_bind_vcpu bind_vcpu; | 735 | struct evtchn_bind_vcpu bind_vcpu; |
709 | int evtchn = evtchn_from_irq(irq); | 736 | int evtchn = evtchn_from_irq(irq); |
710 | 737 | ||
711 | if (!VALID_EVTCHN(evtchn)) | 738 | /* events delivered via platform PCI interrupts are always |
739 | * routed to vcpu 0 */ | ||
740 | if (!VALID_EVTCHN(evtchn) || | ||
741 | (xen_hvm_domain() && !xen_have_vector_callback)) | ||
712 | return -1; | 742 | return -1; |
713 | 743 | ||
714 | /* Send future instances of this interrupt to other vcpu. */ | 744 | /* Send future instances of this interrupt to other vcpu. */ |
@@ -933,6 +963,44 @@ static struct irq_chip xen_dynamic_chip __read_mostly = { | |||
933 | .retrigger = retrigger_dynirq, | 963 | .retrigger = retrigger_dynirq, |
934 | }; | 964 | }; |
935 | 965 | ||
966 | int xen_set_callback_via(uint64_t via) | ||
967 | { | ||
968 | struct xen_hvm_param a; | ||
969 | a.domid = DOMID_SELF; | ||
970 | a.index = HVM_PARAM_CALLBACK_IRQ; | ||
971 | a.value = via; | ||
972 | return HYPERVISOR_hvm_op(HVMOP_set_param, &a); | ||
973 | } | ||
974 | EXPORT_SYMBOL_GPL(xen_set_callback_via); | ||
975 | |||
976 | #ifdef CONFIG_XEN_PVHVM | ||
977 | /* Vector callbacks are better than PCI interrupts to receive event | ||
978 | * channel notifications because we can receive vector callbacks on any | ||
979 | * vcpu and we don't need PCI support or APIC interactions. */ | ||
980 | void xen_callback_vector(void) | ||
981 | { | ||
982 | int rc; | ||
983 | uint64_t callback_via; | ||
984 | if (xen_have_vector_callback) { | ||
985 | callback_via = HVM_CALLBACK_VECTOR(XEN_HVM_EVTCHN_CALLBACK); | ||
986 | rc = xen_set_callback_via(callback_via); | ||
987 | if (rc) { | ||
988 | printk(KERN_ERR "Request for Xen HVM callback vector" | ||
989 | " failed.\n"); | ||
990 | xen_have_vector_callback = 0; | ||
991 | return; | ||
992 | } | ||
993 | printk(KERN_INFO "Xen HVM callback vector for event delivery is " | ||
994 | "enabled\n"); | ||
995 | /* in the restore case the vector has already been allocated */ | ||
996 | if (!test_bit(XEN_HVM_EVTCHN_CALLBACK, used_vectors)) | ||
997 | alloc_intr_gate(XEN_HVM_EVTCHN_CALLBACK, xen_hvm_callback_vector); | ||
998 | } | ||
999 | } | ||
1000 | #else | ||
1001 | void xen_callback_vector(void) {} | ||
1002 | #endif | ||
1003 | |||
936 | void __init xen_init_IRQ(void) | 1004 | void __init xen_init_IRQ(void) |
937 | { | 1005 | { |
938 | int i; | 1006 | int i; |
@@ -947,5 +1015,10 @@ void __init xen_init_IRQ(void) | |||
947 | for (i = 0; i < NR_EVENT_CHANNELS; i++) | 1015 | for (i = 0; i < NR_EVENT_CHANNELS; i++) |
948 | mask_evtchn(i); | 1016 | mask_evtchn(i); |
949 | 1017 | ||
950 | irq_ctx_init(smp_processor_id()); | 1018 | if (xen_hvm_domain()) { |
1019 | xen_callback_vector(); | ||
1020 | native_init_IRQ(); | ||
1021 | } else { | ||
1022 | irq_ctx_init(smp_processor_id()); | ||
1023 | } | ||
951 | } | 1024 | } |