diff options
author | Sheng Yang <sheng@linux.intel.com> | 2010-05-14 07:40:51 -0400 |
---|---|---|
committer | Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com> | 2010-07-22 19:45:59 -0400 |
commit | 38e20b07efd541a959de367dc90a17f92ce2e8a6 (patch) | |
tree | bb087e243bf6e707dd063be2024a3b90de2f9413 /drivers/xen | |
parent | bee6ab53e652a414af20392899879b58cd80d033 (diff) |
x86/xen: event channels delivery on HVM.
Set the callback to receive evtchns from Xen, using the
callback vector delivery mechanism.
The traditional way for receiving event channel notifications from Xen
is via the interrupts from the platform PCI device.
The callback vector is a newer alternative that allow us to receive
notifications on any vcpu and doesn't need any PCI support: we allocate
a vector exclusively to receive events, in the vector handler we don't
need to interact with the vlapic, therefore we avoid a VMEXIT.
Signed-off-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
Signed-off-by: Sheng Yang <sheng@linux.intel.com>
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Diffstat (limited to 'drivers/xen')
-rw-r--r-- | drivers/xen/events.c | 70 |
1 files changed, 63 insertions, 7 deletions
diff --git a/drivers/xen/events.c b/drivers/xen/events.c index db8f506817f0..d659480125f0 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 |
@@ -617,17 +622,13 @@ static DEFINE_PER_CPU(unsigned, xed_nesting_count); | |||
617 | * a bitset of words which contain pending event bits. The second | 622 | * a bitset of words which contain pending event bits. The second |
618 | * level is a bitset of pending events themselves. | 623 | * level is a bitset of pending events themselves. |
619 | */ | 624 | */ |
620 | void xen_evtchn_do_upcall(struct pt_regs *regs) | 625 | static void __xen_evtchn_do_upcall(void) |
621 | { | 626 | { |
622 | int cpu = get_cpu(); | 627 | int cpu = get_cpu(); |
623 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
624 | struct shared_info *s = HYPERVISOR_shared_info; | 628 | struct shared_info *s = HYPERVISOR_shared_info; |
625 | struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); | 629 | struct vcpu_info *vcpu_info = __get_cpu_var(xen_vcpu); |
626 | unsigned count; | 630 | unsigned count; |
627 | 631 | ||
628 | exit_idle(); | ||
629 | irq_enter(); | ||
630 | |||
631 | do { | 632 | do { |
632 | unsigned long pending_words; | 633 | unsigned long pending_words; |
633 | 634 | ||
@@ -667,10 +668,26 @@ void xen_evtchn_do_upcall(struct pt_regs *regs) | |||
667 | } while(count != 1); | 668 | } while(count != 1); |
668 | 669 | ||
669 | out: | 670 | out: |
671 | |||
672 | put_cpu(); | ||
673 | } | ||
674 | |||
675 | void xen_evtchn_do_upcall(struct pt_regs *regs) | ||
676 | { | ||
677 | struct pt_regs *old_regs = set_irq_regs(regs); | ||
678 | |||
679 | exit_idle(); | ||
680 | irq_enter(); | ||
681 | |||
682 | __xen_evtchn_do_upcall(); | ||
683 | |||
670 | irq_exit(); | 684 | irq_exit(); |
671 | set_irq_regs(old_regs); | 685 | set_irq_regs(old_regs); |
686 | } | ||
672 | 687 | ||
673 | put_cpu(); | 688 | void xen_hvm_evtchn_do_upcall(void) |
689 | { | ||
690 | __xen_evtchn_do_upcall(); | ||
674 | } | 691 | } |
675 | 692 | ||
676 | /* Rebind a new event channel to an existing irq. */ | 693 | /* Rebind a new event channel to an existing irq. */ |
@@ -933,6 +950,40 @@ static struct irq_chip xen_dynamic_chip __read_mostly = { | |||
933 | .retrigger = retrigger_dynirq, | 950 | .retrigger = retrigger_dynirq, |
934 | }; | 951 | }; |
935 | 952 | ||
953 | int xen_set_callback_via(uint64_t via) | ||
954 | { | ||
955 | struct xen_hvm_param a; | ||
956 | a.domid = DOMID_SELF; | ||
957 | a.index = HVM_PARAM_CALLBACK_IRQ; | ||
958 | a.value = via; | ||
959 | return HYPERVISOR_hvm_op(HVMOP_set_param, &a); | ||
960 | } | ||
961 | EXPORT_SYMBOL_GPL(xen_set_callback_via); | ||
962 | |||
963 | /* Vector callbacks are better than PCI interrupts to receive event | ||
964 | * channel notifications because we can receive vector callbacks on any | ||
965 | * vcpu and we don't need PCI support or APIC interactions. */ | ||
966 | void xen_callback_vector(void) | ||
967 | { | ||
968 | int rc; | ||
969 | uint64_t callback_via; | ||
970 | if (xen_have_vector_callback) { | ||
971 | callback_via = HVM_CALLBACK_VECTOR(XEN_HVM_EVTCHN_CALLBACK); | ||
972 | rc = xen_set_callback_via(callback_via); | ||
973 | if (rc) { | ||
974 | printk(KERN_ERR "Request for Xen HVM callback vector" | ||
975 | " failed.\n"); | ||
976 | xen_have_vector_callback = 0; | ||
977 | return; | ||
978 | } | ||
979 | printk(KERN_INFO "Xen HVM callback vector for event delivery is " | ||
980 | "enabled\n"); | ||
981 | /* in the restore case the vector has already been allocated */ | ||
982 | if (!test_bit(XEN_HVM_EVTCHN_CALLBACK, used_vectors)) | ||
983 | alloc_intr_gate(XEN_HVM_EVTCHN_CALLBACK, xen_hvm_callback_vector); | ||
984 | } | ||
985 | } | ||
986 | |||
936 | void __init xen_init_IRQ(void) | 987 | void __init xen_init_IRQ(void) |
937 | { | 988 | { |
938 | int i; | 989 | int i; |
@@ -947,5 +998,10 @@ void __init xen_init_IRQ(void) | |||
947 | for (i = 0; i < NR_EVENT_CHANNELS; i++) | 998 | for (i = 0; i < NR_EVENT_CHANNELS; i++) |
948 | mask_evtchn(i); | 999 | mask_evtchn(i); |
949 | 1000 | ||
950 | irq_ctx_init(smp_processor_id()); | 1001 | if (xen_hvm_domain()) { |
1002 | xen_callback_vector(); | ||
1003 | native_init_IRQ(); | ||
1004 | } else { | ||
1005 | irq_ctx_init(smp_processor_id()); | ||
1006 | } | ||
951 | } | 1007 | } |