xen/events: don't bind non-percpu VIRQs with percpu chip
commit 77bb3dfdc0
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>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
c018f62178
commit
8d3cbb9096
|
@ -289,7 +289,7 @@ static int xen_initial_domain_console_init(void)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0);
|
info->irq = bind_virq_to_irq(VIRQ_CONSOLE, 0, false);
|
||||||
info->vtermno = HVC_COOKIE;
|
info->vtermno = HVC_COOKIE;
|
||||||
|
|
||||||
spin_lock(&xencons_lock);
|
spin_lock(&xencons_lock);
|
||||||
|
|
|
@ -973,7 +973,7 @@ unsigned xen_evtchn_nr_channels(void)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(xen_evtchn_nr_channels);
|
EXPORT_SYMBOL_GPL(xen_evtchn_nr_channels);
|
||||||
|
|
||||||
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;
|
struct evtchn_bind_virq bind_virq;
|
||||||
int evtchn, irq, ret;
|
int evtchn, irq, ret;
|
||||||
|
@ -987,8 +987,12 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
|
if (percpu)
|
||||||
handle_percpu_irq, "virq");
|
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.virq = virq;
|
||||||
bind_virq.vcpu = cpu;
|
bind_virq.vcpu = cpu;
|
||||||
|
@ -1078,7 +1082,7 @@ int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
|
||||||
{
|
{
|
||||||
int irq, retval;
|
int irq, retval;
|
||||||
|
|
||||||
irq = bind_virq_to_irq(virq, cpu);
|
irq = bind_virq_to_irq(virq, cpu, irqflags & IRQF_PERCPU);
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
return irq;
|
return irq;
|
||||||
retval = request_irq(irq, handler, irqflags, devname, dev_id);
|
retval = request_irq(irq, handler, irqflags, devname, dev_id);
|
||||||
|
|
|
@ -14,7 +14,7 @@ int bind_evtchn_to_irqhandler(unsigned int evtchn,
|
||||||
irq_handler_t handler,
|
irq_handler_t handler,
|
||||||
unsigned long irqflags, const char *devname,
|
unsigned long irqflags, const char *devname,
|
||||||
void *dev_id);
|
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,
|
int bind_virq_to_irqhandler(unsigned int virq, unsigned int cpu,
|
||||||
irq_handler_t handler,
|
irq_handler_t handler,
|
||||||
unsigned long irqflags, const char *devname,
|
unsigned long irqflags, const char *devname,
|
||||||
|
|
Loading…
Reference in New Issue