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>
This commit is contained in:
Родитель
ffb7dbed47
Коммит
77bb3dfdc0
|
@ -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);
|
||||||
|
|
|
@ -957,7 +957,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;
|
||||||
|
@ -971,8 +971,12 @@ int bind_virq_to_irq(unsigned int virq, unsigned int cpu)
|
||||||
if (irq < 0)
|
if (irq < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
if (percpu)
|
||||||
irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
|
irq_set_chip_and_handler_name(irq, &xen_percpu_chip,
|
||||||
handle_percpu_irq, "virq");
|
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;
|
||||||
|
@ -1062,7 +1066,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);
|
||||||
|
|
|
@ -17,7 +17,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,
|
||||||
|
|
Загрузка…
Ссылка в новой задаче