]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
xen/events: don't bind non-percpu VIRQs with percpu chip
authorDavid Vrabel <david.vrabel@citrix.com>
Tue, 19 May 2015 17:40:49 +0000 (18:40 +0100)
committerBen Hutchings <ben@decadent.org.uk>
Thu, 6 Aug 2015 23:32:12 +0000 (00:32 +0100)
commit 77bb3dfdc0d554befad58fdefbc41be5bc3ed38a upstream.

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>
[bwh: Backported to 3.2: adjust filename, context, indentation]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/tty/hvc/hvc_xen.c
drivers/xen/events.c
include/xen/events.h

index 52fdf60bdbe2db44a7457a1392de002d3f79d79d..df8f8e080e313c3e115c6b3d387675c0f147b3d0 100644 (file)
@@ -167,7 +167,7 @@ static int __init xen_hvc_init(void)
 
        if (xen_initial_domain()) {
                ops = &dom0_hvc_ops;
-               xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0);
+               xencons_irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
        } else {
                if (!xen_start_info->console.domU.evtchn)
                        return -ENODEV;
index f6227cc86f006c6e09376fe6c104ccd1904a2a3c..bcf7711784a5f780f5aac0880bcc9e3cad5fd096 100644 (file)
@@ -895,7 +895,7 @@ static int find_virq(unsigned int virq, unsigned int cpu)
        return rc;
 }
 
-int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
+int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu)
 {
        struct evtchn_bind_virq bind_virq;
        int evtchn, irq, ret;
@@ -909,8 +909,12 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
                if (irq == -1)
                        goto out;
 
-               irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
-                                             handle_percpu_irq, "virq");
+               if (percpu)
+                       irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
+                                                     handle_percpu_irq, "virq");
+               else
+                       irq_set_chip_and_handler_name(irq, &xen_dynamic_chip,
+                                                     handle_edge_irq, "virq");
 
                bind_virq.virq = virq;
                bind_virq.vcpu = cpu;
@@ -1023,7 +1027,7 @@ int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
 {
        int irq, retval;
 
-       irq = bind_virq_to_irq(virq, cpu);
+       irq = bind_virq_to_irq(virq, cpu, irqflags & IRQF_PERCPU);
        if (irq < 0)
                return irq;
        retval = request_irq(irq, handler, irqflags, devname, dev_id);
index 8f3d62251220ca0c7bd67233997148e0844c67c8..89b672dad1bf9f2e32fcef92cdb900ff18387fb0 100644 (file)
@@ -12,7 +12,7 @@ int bind_evtchn_to_irqhandler(unsigned int evtchn,
                              irq_handler_t handler,
                              unsigned long irqflags, const char *devname,
                              void *dev_id);
-int bind_virq_to_irq(unsigned int virq, unsigned int cpu);
+int bind_virq_to_irq(unsigned int virq, unsigned int cpu, bool percpu);
 int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
                            irq_handler_t handler,
                            unsigned long irqflags, const char *devname,