summaryrefslogtreecommitdiffstats
path: root/drivers/uio
diff options
context:
space:
mode:
authorThommy Jakobsson <thommyj@gmail.com>2020-06-28 16:12:29 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2020-07-03 11:02:21 +0200
commit415abcdfd4b8326d679cd0c78bee00b5ceac9718 (patch)
tree6d01c0d43e471b907912f8f25274bd6d5469d974 /drivers/uio
parent7aca462b133300c16213e214bb96e516f1f2784e (diff)
downloadlinux-415abcdfd4b8326d679cd0c78bee00b5ceac9718.tar.bz2
uio: disable lazy irq disable to avoid double fire
uio_pdrv_genirq and uio_dmem_genirq interrupts are handled in userspace. So the condition for the interrupt hasn't normally not been cleared when top half returns. disable_irq_nosync is called in top half, but since that normally is lazy the irq isn't actually disabled. For level triggered interrupts this will always result in a spurious additional fire since the level in to the interrupt controller still is active. The actual interrupt handler isn't run though since this spurious irq is just recorded, and later on discared (for level). This commit disables lazy masking for level triggered interrupts. It leaves edge triggered interrupts as before, because they work with the lazy scheme. All other UIO drivers already seem to clear the interrupt cause at driver levels. Example of double fire. First goes all the way up to uio_pdrv_genirq_handler, second is terminated in handle_fasteoi_irq and marked as pending. <idle>-0 [000] d... 8.245870: gic_handle_irq: irq 29 <idle>-0 [000] d.h. 8.245873: uio_pdrv_genirq_handler: disable irq 29 <idle>-0 [000] d... 8.245878: gic_handle_irq: irq 29 <idle>-0 [000] d.h. 8.245880: handle_fasteoi_irq: irq 29 PENDING HInt-34 [001] d... 8.245897: uio_pdrv_genirq_irqcontrol: enable irq 29 Tested on 5.7rc2 using uio_pdrv_genirq and a custom Xilinx MPSoC board. Signed-off-by: Thommy Jakobsson <thommyj@gmail.com> Link: https://lore.kernel.org/r/20200628141229.16121-1-thommyj@gmail.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/uio')
-rw-r--r--drivers/uio/uio_dmem_genirq.c19
-rw-r--r--drivers/uio/uio_pdrv_genirq.c18
2 files changed, 37 insertions, 0 deletions
diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c
index 6e27fe4fcca3..ec7f66f4555a 100644
--- a/drivers/uio/uio_dmem_genirq.c
+++ b/drivers/uio/uio_dmem_genirq.c
@@ -20,6 +20,7 @@
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -199,6 +200,24 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev)
goto bad1;
uioinfo->irq = ret;
}
+
+ if (uioinfo->irq) {
+ struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq);
+
+ /*
+ * If a level interrupt, dont do lazy disable. Otherwise the
+ * irq will fire again since clearing of the actual cause, on
+ * device level, is done in userspace
+ * irqd_is_level_type() isn't used since isn't valid until
+ * irq is configured.
+ */
+ if (irq_data &&
+ irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) {
+ dev_dbg(&pdev->dev, "disable lazy unmask\n");
+ irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY);
+ }
+ }
+
uiomem = &uioinfo->mem[0];
for (i = 0; i < pdev->num_resources; ++i) {
diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c
index 11e11df0daf8..5f396c37a34f 100644
--- a/drivers/uio/uio_pdrv_genirq.c
+++ b/drivers/uio/uio_pdrv_genirq.c
@@ -20,6 +20,7 @@
#include <linux/stringify.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+#include <linux/irq.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -171,6 +172,23 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev)
}
}
+ if (uioinfo->irq) {
+ struct irq_data *irq_data = irq_get_irq_data(uioinfo->irq);
+
+ /*
+ * If a level interrupt, dont do lazy disable. Otherwise the
+ * irq will fire again since clearing of the actual cause, on
+ * device level, is done in userspace
+ * irqd_is_level_type() isn't used since isn't valid until
+ * irq is configured.
+ */
+ if (irq_data &&
+ irqd_get_trigger_type(irq_data) & IRQ_TYPE_LEVEL_MASK) {
+ dev_dbg(&pdev->dev, "disable lazy unmask\n");
+ irq_set_status_flags(uioinfo->irq, IRQ_DISABLE_UNLAZY);
+ }
+ }
+
uiomem = &uioinfo->mem[0];
for (i = 0; i < pdev->num_resources; ++i) {