Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+10 -6
+1 -1
drivers/tty/hvc/hvc_xen.c
··· 289 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 293 info->vtermno = HVC_COOKIE; 294 294 295 295 spin_lock(&xencons_lock);
+8 -4
drivers/xen/events/events_base.c
··· 957 957 } 958 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 962 struct evtchn_bind_virq bind_virq; 963 963 int evtchn, irq, ret; ··· 971 971 if (irq < 0) 972 972 goto out; 973 973 974 - irq_set_chip_and_handler_name(irq, &xen_percpu_chip, 975 - handle_percpu_irq, "virq"); 974 + if (percpu) 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 981 bind_virq.virq = virq; 978 982 bind_virq.vcpu = cpu; ··· 1066 1062 { 1067 1063 int irq, retval; 1068 1064 1069 - irq = bind_virq_to_irq(virq, cpu); 1065 + irq = bind_virq_to_irq(virq, cpu, irqflags & IRQF_PERCPU); 1070 1066 if (irq < 0) 1071 1067 return irq; 1072 1068 retval = request_irq(irq, handler, irqflags, devname, dev_id);
+1 -1
include/xen/events.h
··· 17 17 irq_handler_t handler, 18 18 unsigned long irqflags, const char *devname, 19 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 21 int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu, 22 22 irq_handler_t handler, 23 23 unsigned long irqflags, const char *devname,