summaryrefslogtreecommitdiffstats
path: root/debian/patches-rt/softirq-Avoid-a-cancel-dead-lock-in-tasklet-handling.patch
blob: 4663d89c10a825ce612558899733d2038f1781cd (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
From: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Date: Sat, 22 Jun 2019 00:09:22 +0200
Subject: [PATCH] softirq: Avoid a cancel dead-lock in tasklet handling due to
 preemptible-softirq
Origin: https://www.kernel.org/pub/linux/kernel/projects/rt/5.2/older/patches-5.2.17-rt9.tar.xz

A pending / active tasklet which is preempted by a task on the same CPU
will spin indefinitely becauase the tasklet makes no progress.
To avoid this deadlock we can disable BH which will acquire the
softirq-lock which will force the completion of the softirq and so the
tasklet.
The BH off/on in tasklet_kill() will force tasklets which are not yet
running but scheduled (because ksoftirqd was preempted before it could
start the tasklet).
The BH off/on in  tasklet_unlock_wait() will force tasklets which got
preempted while running.

Signed-off-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
---
 include/linux/interrupt.h |    5 ++++-
 kernel/softirq.c          |    3 ++-
 2 files changed, 6 insertions(+), 2 deletions(-)

--- a/include/linux/interrupt.h
+++ b/include/linux/interrupt.h
@@ -625,7 +625,10 @@ static inline void tasklet_unlock(struct
 
 static inline void tasklet_unlock_wait(struct tasklet_struct *t)
 {
-	while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
+	while (test_bit(TASKLET_STATE_RUN, &(t)->state)) {
+		local_bh_disable();
+		local_bh_enable();
+	}
 }
 #else
 #define tasklet_trylock(t) 1
--- a/kernel/softirq.c
+++ b/kernel/softirq.c
@@ -711,7 +711,8 @@ void tasklet_kill(struct tasklet_struct
 
 	while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {
 		do {
-			yield();
+			local_bh_disable();
+			local_bh_enable();
 		} while (test_bit(TASKLET_STATE_SCHED, &t->state));
 	}
 	tasklet_unlock_wait(t);