diff options
author | David Vrabel <david.vrabel@citrix.com> | 2015-05-19 13:40:49 -0400 |
---|---|---|
committer | David Vrabel <david.vrabel@citrix.com> | 2015-05-19 14:55:36 -0400 |
commit | 77bb3dfdc0d554befad58fdefbc41be5bc3ed38a (patch) | |
tree | 845e60544a5b07d4adc6beb6249997c909575c1c | |
parent | ffb7dbed47da6ac4460b606a3feee295bbe4d9e2 (diff) |
xen/events: don't bind non-percpu VIRQs with percpu chip
A non-percpu VIRQ (e.g., VIRQ_CONSOLE) may be freed on a different
VCPU than it is bound to. This can result in a race between
handle_percpu_irq() and removing the action in __free_irq() because
handle_percpu_irq() does not take desc->lock. The interrupt handler
sees a NULL action and oopses.
Only use the percpu chip/handler for per-CPU VIRQs (like VIRQ_TIMER).
# cat /proc/interrupts | grep virq
40: 87246 0 xen-percpu-virq timer0
44: 0 0 xen-percpu-virq debug0
47: 0 20995 xen-percpu-virq timer1
51: 0 0 xen-percpu-virq debug1
69: 0 0 xen-dyn-virq xen-pcpu
74: 0 0 xen-dyn-virq mce
75: 29 0 xen-dyn-virq hvc_console
Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Cc: <stable@vger.kernel.org>
-rw-r--r-- | drivers/tty/hvc/hvc_xen.c | 2 | ||||
-rw-r--r-- | drivers/xen/events/events_base.c | 12 | ||||
-rw-r--r-- | include/xen/events.h | 2 |
3 files changed, 10 insertions, 6 deletions
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c index 5bab1c684bb1..7a3d146a5f0e 100644 --- a/drivers/tty/hvc/hvc_xen.c +++ b/drivers/tty/hvc/hvc_xen.c | |||
@@ -289,7 +289,7 @@ static int xen_initial_domain_console_init(void) | |||
289 | return -ENOMEM; | 289 | return -ENOMEM; |
290 | } | 290 | } |
291 | 291 | ||
292 | info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0); | 292 | info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false); |
293 | info->vtermno = HVC_COOKIE; | 293 | info->vtermno = HVC_COOKIE; |
294 | 294 | ||
295 | spin_lock(&xencons_lock); | 295 | spin_lock(&xencons_lock); |
diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 2b8553bd8715..38387950490e 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c | |||
@@ -957,7 +957,7 @@ unsigned xen_evtchn_nr_channels(void) | |||
957 | } | 957 | } |
958 | EXPORT_SYMBOL_GPL(xen_evtchn_nr_channels); | 958 | EXPORT_SYMBOL_GPL(xen_evtchn_nr_channels); |
959 | 959 | ||
960 | int bind_virq_to_irq(unsigned int virq, unsigned int cpu) | 960 | int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu) |
961 | { | 961 | { |
962 | struct evtchn_bind_virq bind_virq; | 962 | struct evtchn_bind_virq bind_virq; |
963 | int evtchn, irq, ret; | 963 | int evtchn, irq, ret; |
@@ -971,8 +971,12 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu) | |||
971 | if (irq < 0) | 971 | if (irq < 0) |
972 | goto out; | 972 | goto out; |
973 | 973 | ||
974 | irq_set_chip_and_handler_name(irq, &xen_percpu_chip, | 974 | if (percpu) |
975 | handle_percpu_irq, "virq"); | 975 | irq_set_chip_and_handler_name(irq, &xen_percpu_chip, |
976 | handle_percpu_irq, "virq"); | ||
977 | else | ||
978 | irq_set_chip_and_handler_name(irq, &xen_dynamic_chip, | ||
979 | handle_edge_irq, "virq"); | ||
976 | 980 | ||
977 | bind_virq.virq = virq; | 981 | bind_virq.virq = virq; |
978 | bind_virq.vcpu = cpu; | 982 | bind_virq.vcpu = cpu; |
@@ -1062,7 +1066,7 @@ int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, | |||
1062 | { | 1066 | { |
1063 | int irq, retval; | 1067 | int irq, retval; |
1064 | 1068 | ||
1065 | irq = bind_virq_to_irq(virq, cpu); | 1069 | irq = bind_virq_to_irq(virq, cpu, irqflags & IRQF_PERCPU); |
1066 | if (irq < 0) | 1070 | if (irq < 0) |
1067 | return irq; | 1071 | return irq; |
1068 | retval = request_irq(irq, handler, irqflags, devname, dev_id); | 1072 | retval = request_irq(irq, handler, irqflags, devname, dev_id); |
diff --git a/include/xen/events.h b/include/xen/events.h index 5321cd9636e6..7d95fdf9cf3e 100644 --- a/include/xen/events.h +++ b/include/xen/events.h | |||
@@ -17,7 +17,7 @@ int bind_evtchn_to_irqhandler(unsigned int evtchn, | |||
17 | irq_handler_t handler, | 17 | irq_handler_t handler, |
18 | unsigned long irqflags, const char *devname, | 18 | unsigned long irqflags, const char *devname, |
19 | void *dev_id); | 19 | void *dev_id); |
20 | int bind_virq_to_irq(unsigned int virq, unsigned int cpu); | 20 | int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu); |
21 | int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, | 21 | int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, |
22 | irq_handler_t handler, | 22 | irq_handler_t handler, |
23 | unsigned long irqflags, const char *devname, | 23 | unsigned long irqflags, const char *devname, |