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

Configure Feed

Select the types of activity you want to include in your feed.

sparc64: Fix lost interrupts on sun4u.

Based upon a report by Meelis Roos.

Sparc64 SBUS and PCI controllers use a combination of IMAP and ICLR
registers to manage device interrupts.

The IMAP register contains the "valid" enable bit as well as CPU
targetting information. Whereas the ICLR register is written with
zero at the end of handling an interrupt to reset the state machine
for that interrupt to IDLE so it can be sent again.

For PCI slot and SBUS slot devices we can have multiple interrupts
sharing the same IMAP register. There are individual ICLR registers
but only one IMAP register for managing those.

We represent each shared case with individual virtual IRQs so the
generic IRQ layer thinks there is only one user of the IRQ instance.

In such shared IMAP cases this is wrong, so if there are multiple
active users then a free_irq() call will prematurely turn off the
interrupt by clearing the Valid bit in the IMAP register even though
there are other active users.

Fix this by simply doing nothing in sun4u_disable_irq() and checking
IRQF_DISABLED during IRQ dispatch.

This situation doesn't exist in the hypervisor sun4v cases, so I left
those alone.

Tested-by: Meelis Roos <mroos@linux.ee>
Signed-off-by: David S. Miller <davem@davemloft.net>

+19 -10
+19 -10
arch/sparc/kernel/irq_64.c
··· 323 323 sun4u_irq_enable(virt_irq); 324 324 } 325 325 326 + /* Don't do anything. The desc->status check for IRQ_DISABLED in 327 + * handler_irq() will skip the handler call and that will leave the 328 + * interrupt in the sent state. The next ->enable() call will hit the 329 + * ICLR register to reset the state machine. 330 + * 331 + * This scheme is necessary, instead of clearing the Valid bit in the 332 + * IMAP register, to handle the case of IMAP registers being shared by 333 + * multiple INOs (and thus ICLR registers). Since we use a different 334 + * virtual IRQ for each shared IMAP instance, the generic code thinks 335 + * there is only one user so it prematurely calls ->disable() on 336 + * free_irq(). 337 + * 338 + * We have to provide an explicit ->disable() method instead of using 339 + * NULL to get the default. The reason is that if the generic code 340 + * sees that, it also hooks up a default ->shutdown method which 341 + * invokes ->mask() which we do not want. See irq_chip_set_defaults(). 342 + */ 326 343 static void sun4u_irq_disable(unsigned int virt_irq) 327 344 { 328 - struct irq_handler_data *data = get_irq_chip_data(virt_irq); 329 - 330 - if (likely(data)) { 331 - unsigned long imap = data->imap; 332 - unsigned long tmp = upa_readq(imap); 333 - 334 - tmp &= ~IMAP_VALID; 335 - upa_writeq(tmp, imap); 336 - } 337 345 } 338 346 339 347 static void sun4u_irq_eoi(unsigned int virt_irq) ··· 754 746 755 747 desc = irq_desc + virt_irq; 756 748 757 - desc->handle_irq(virt_irq, desc); 749 + if (!(desc->status & IRQ_DISABLED)) 750 + desc->handle_irq(virt_irq, desc); 758 751 759 752 bucket_pa = next_pa; 760 753 }