| From ff1e22e7a638a0782f54f81a6c9cb139aca2da35 Mon Sep 17 00:00:00 2001 |
| From: Boris Ostrovsky <boris.ostrovsky@oracle.com> |
| Date: Fri, 18 Mar 2016 10:11:07 -0400 |
| Subject: xen/events: Mask a moving irq |
| |
| From: Boris Ostrovsky <boris.ostrovsky@oracle.com> |
| |
| commit ff1e22e7a638a0782f54f81a6c9cb139aca2da35 upstream. |
| |
| Moving an unmasked irq may result in irq handler being invoked on both |
| source and target CPUs. |
| |
| With 2-level this can happen as follows: |
| |
| On source CPU: |
| evtchn_2l_handle_events() -> |
| generic_handle_irq() -> |
| handle_edge_irq() -> |
| eoi_pirq(): |
| irq_move_irq(data); |
| |
| /***** WE ARE HERE *****/ |
| |
| if (VALID_EVTCHN(evtchn)) |
| clear_evtchn(evtchn); |
| |
| If at this moment target processor is handling an unrelated event in |
| evtchn_2l_handle_events()'s loop it may pick up our event since target's |
| cpu_evtchn_mask claims that this event belongs to it *and* the event is |
| unmasked and still pending. At the same time, source CPU will continue |
| executing its own handle_edge_irq(). |
| |
| With FIFO interrupt the scenario is similar: irq_move_irq() may result |
| in a EVTCHNOP_unmask hypercall which, in turn, may make the event |
| pending on the target CPU. |
| |
| We can avoid this situation by moving and clearing the event while |
| keeping event masked. |
| |
| Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com> |
| Signed-off-by: David Vrabel <david.vrabel@citrix.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/xen/events/events_base.c | 28 ++++++++++++++++++++++++---- |
| 1 file changed, 24 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/xen/events/events_base.c |
| +++ b/drivers/xen/events/events_base.c |
| @@ -503,9 +503,19 @@ static void eoi_pirq(struct irq_data *da |
| struct physdev_eoi eoi = { .irq = pirq_from_irq(data->irq) }; |
| int rc = 0; |
| |
| - irq_move_irq(data); |
| + if (!VALID_EVTCHN(evtchn)) |
| + return; |
| |
| - if (VALID_EVTCHN(evtchn)) |
| + if (unlikely(irqd_is_setaffinity_pending(data))) { |
| + int masked = test_and_set_mask(evtchn); |
| + |
| + clear_evtchn(evtchn); |
| + |
| + irq_move_masked_irq(data); |
| + |
| + if (!masked) |
| + unmask_evtchn(evtchn); |
| + } else |
| clear_evtchn(evtchn); |
| |
| if (pirq_needs_eoi(data->irq)) { |
| @@ -1395,9 +1405,19 @@ static void ack_dynirq(struct irq_data * |
| { |
| int evtchn = evtchn_from_irq(data->irq); |
| |
| - irq_move_irq(data); |
| + if (!VALID_EVTCHN(evtchn)) |
| + return; |
| |
| - if (VALID_EVTCHN(evtchn)) |
| + if (unlikely(irqd_is_setaffinity_pending(data))) { |
| + int masked = test_and_set_mask(evtchn); |
| + |
| + clear_evtchn(evtchn); |
| + |
| + irq_move_masked_irq(data); |
| + |
| + if (!masked) |
| + unmask_evtchn(evtchn); |
| + } else |
| clear_evtchn(evtchn); |
| } |
| |