summaryrefslogtreecommitdiffstats
path: root/bsp/meta-rcar/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0469-rcar_canfd-fix-one-more-interrupt-storm-window.patch
blob: 506ff276a275830280713693d7b62c89de5b76bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
From bfdc3bf828002a0550b8183a67879fc3a9246795 Mon Sep 17 00:00:00 2001
From: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
Date: Thu, 6 Feb 2020 23:13:19 +0300
Subject: [PATCH] rcar_canfd: fix one more interrupt storm window

When more than one rcar_canfd channel works simultaneously, same irq and
same irq handler, rcar_canfd_global_interrupt(), is shared between them
all. Entry into this handler while Rx interrupt for one channel is masked
per NAPI, becomes a very frequent event.

Code makes an attempt to alter interrupt mask only when NAPI suggests to
do so. However that code is racy, and with frequent call to handler with
masked interrupt, this race actually happens.

The race window is:
- after code in napi_complete_done() clears NAPIF_STATE_SCHED flag inside
  NAPI state, but before napi_complete_done() returns true, side-called
  interrupt handler detects pending (although masked) Rx, calls
  napi_schedule_prep(), and NAPIF_STATE_SCHED flag is re-set,
- then napi_complete_done() returns true and Rx interrupt gets unmasked,
- then unmasked interrupt fires, but this time NAPIF_STATE_SCHED flag
  is already set, so napi_schedule_prep() returns false,
- which causes return from interrupt handler without touching interrupt,
  and IRQ storm is entered.

To fix it, active Rx interrupt must be UNCONDITIONALLY masked inside the
handler. Failing to mask active interrupt immediately results into IRQ
storm, regardless of NAPI state or whatever other conditions.

Once napi_schedule_prep() is called after this unconditional masking, future
call to NAPI poller and evential interrupt unmask is guaranteed.

Signed-off-by: Nikita Yushchenko <nikita.yoush@cogentembedded.com>
---
 drivers/net/can/rcar/rcar_canfd.c | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/net/can/rcar/rcar_canfd.c b/drivers/net/can/rcar/rcar_canfd.c
index 0017ab9..056a5f6 100644
--- a/drivers/net/can/rcar/rcar_canfd.c
+++ b/drivers/net/can/rcar/rcar_canfd.c
@@ -1101,13 +1101,16 @@ static irqreturn_t rcar_canfd_global_interrupt(int irq, void *dev_id)
 		/* Handle Rx interrupts */
 		sts = rcar_canfd_read(priv->base, RCANFD_RFSTS(ridx));
 		if (likely(sts & RCANFD_RFSTS_RFIF)) {
-			if (napi_schedule_prep(&priv->napi)) {
-				/* Disable Rx FIFO interrupts */
-				rcar_canfd_clear_bit(priv->base,
-						     RCANFD_RFCC(ridx),
-						     RCANFD_RFCC_RFIE);
+			/* If Rx FIFO interrupt is there, it must be masked
+			 * UNCONDITIONALLY, otherwise IRQ storm will start */
+			rcar_canfd_clear_bit(priv->base,
+					     RCANFD_RFCC(ridx),
+					     RCANFD_RFCC_RFIE);
+			/* The above calls ensure that napi polling will be
+			 * called sometime AFTER the above call, which
+			 * eventually ensures interrupt re-enable */
+			if (napi_schedule_prep(&priv->napi))
 				__napi_schedule(&priv->napi);
-			}
 		}
 	}
 	return IRQ_HANDLED;
-- 
2.7.4