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

powerpc/opal-irqchip: Fix double endian conversion

The OPAL event calls return a mask of events that are active in big
endian format. This is checked when unmasking the events in the
irqchip by comparison with a cached value. The cached value was stored
in big endian format but should've been converted to CPU endian
first.

This bug leads to OPAL event delivery being delayed or dropped on some
systems. Symptoms may include a non-functional console.

The bug is fixed by calling opal_handle_events(...) instead of
duplicating code in opal_event_unmask(...).

Fixes: 9f0fd0499d30 ("powerpc/powernv: Add a virtual irqchip for opal events")
Cc: stable@vger.kernel.org # v4.2+
Reported-by: Douglas L Lehr <dllehr@us.ibm.com>
Signed-off-by: Alistair Popple <alistair@popple.id.au>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>

authored by

Alistair Popple and committed by
Michael Ellerman
25642e14 7f821fc9

+29 -29
+29 -29
arch/powerpc/platforms/powernv/opal-irqchip.c
··· 43 43 static unsigned int *opal_irqs; 44 44 45 45 static void opal_handle_irq_work(struct irq_work *work); 46 - static __be64 last_outstanding_events; 46 + static u64 last_outstanding_events; 47 47 static struct irq_work opal_event_irq_work = { 48 48 .func = opal_handle_irq_work, 49 49 }; 50 + 51 + void opal_handle_events(uint64_t events) 52 + { 53 + int virq, hwirq = 0; 54 + u64 mask = opal_event_irqchip.mask; 55 + 56 + if (!in_irq() && (events & mask)) { 57 + last_outstanding_events = events; 58 + irq_work_queue(&opal_event_irq_work); 59 + return; 60 + } 61 + 62 + while (events & mask) { 63 + hwirq = fls64(events) - 1; 64 + if (BIT_ULL(hwirq) & mask) { 65 + virq = irq_find_mapping(opal_event_irqchip.domain, 66 + hwirq); 67 + if (virq) 68 + generic_handle_irq(virq); 69 + } 70 + events &= ~BIT_ULL(hwirq); 71 + } 72 + } 50 73 51 74 static void opal_event_mask(struct irq_data *d) 52 75 { ··· 78 55 79 56 static void opal_event_unmask(struct irq_data *d) 80 57 { 58 + __be64 events; 59 + 81 60 set_bit(d->hwirq, &opal_event_irqchip.mask); 82 61 83 - opal_poll_events(&last_outstanding_events); 84 - if (last_outstanding_events & opal_event_irqchip.mask) 85 - /* Need to retrigger the interrupt */ 86 - irq_work_queue(&opal_event_irq_work); 62 + opal_poll_events(&events); 63 + opal_handle_events(be64_to_cpu(events)); 87 64 } 88 65 89 66 static int opal_event_set_type(struct irq_data *d, unsigned int flow_type) ··· 119 96 return 0; 120 97 } 121 98 122 - void opal_handle_events(uint64_t events) 123 - { 124 - int virq, hwirq = 0; 125 - u64 mask = opal_event_irqchip.mask; 126 - 127 - if (!in_irq() && (events & mask)) { 128 - last_outstanding_events = events; 129 - irq_work_queue(&opal_event_irq_work); 130 - return; 131 - } 132 - 133 - while (events & mask) { 134 - hwirq = fls64(events) - 1; 135 - if (BIT_ULL(hwirq) & mask) { 136 - virq = irq_find_mapping(opal_event_irqchip.domain, 137 - hwirq); 138 - if (virq) 139 - generic_handle_irq(virq); 140 - } 141 - events &= ~BIT_ULL(hwirq); 142 - } 143 - } 144 - 145 99 static irqreturn_t opal_interrupt(int irq, void *data) 146 100 { 147 101 __be64 events; ··· 131 131 132 132 static void opal_handle_irq_work(struct irq_work *work) 133 133 { 134 - opal_handle_events(be64_to_cpu(last_outstanding_events)); 134 + opal_handle_events(last_outstanding_events); 135 135 } 136 136 137 137 static int opal_event_match(struct irq_domain *h, struct device_node *node,