summaryrefslogtreecommitdiffstats
path: root/meta-agl-devel/meta-agl-jailhouse/recipes-kernel/linux/linux/0015-ivshmem-net-fix-race-in-state-machine.patch
blob: ecb5c485325398ab207f16dc2b0f1550d8918a44 (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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
From 1349a457e5e33580fb26afab087e6b932b1a4372 Mon Sep 17 00:00:00 2001
From: Mans Rullgard <mans@mansr.com>
Date: Thu, 24 Nov 2016 18:46:41 +0000
Subject: [PATCH 15/32] ivshmem-net: fix race in state machine

---
 drivers/net/ivshmem-net.c | 60 ++++++++++++++++++++++++-----------------------
 1 file changed, 31 insertions(+), 29 deletions(-)

diff --git a/drivers/net/ivshmem-net.c b/drivers/net/ivshmem-net.c
index a535cb71adde..acd00e47acd7 100644
--- a/drivers/net/ivshmem-net.c
+++ b/drivers/net/ivshmem-net.c
@@ -36,6 +36,8 @@
 #define IVSHM_NET_STATE_READY	2
 #define IVSHM_NET_STATE_RUN	3
 
+#define IVSHM_NET_FLAG_RUN	0
+
 #define IVSHM_NET_MTU_MIN 256
 #define IVSHM_NET_MTU_MAX 65535
 #define IVSHM_NET_MTU_DEF 16384
@@ -96,6 +98,8 @@ struct ivshm_net {
 	u32 lstate;
 	u32 rstate;
 
+	unsigned long flags;
+
 	struct workqueue_struct *state_wq;
 	struct work_struct state_work;
 
@@ -529,12 +533,32 @@ static void ivshm_net_run(struct net_device *ndev)
 {
 	struct ivshm_net *in = netdev_priv(ndev);
 
+	if (in->lstate < IVSHM_NET_STATE_READY)
+		return;
+
+	if (!netif_running(ndev))
+		return;
+
+	if (test_and_set_bit(IVSHM_NET_FLAG_RUN, &in->flags))
+		return;
+
 	netif_start_queue(ndev);
 	napi_enable(&in->napi);
 	napi_schedule(&in->napi);
 	ivshm_net_set_state(in, IVSHM_NET_STATE_RUN);
 }
 
+static void ivshm_net_do_stop(struct net_device *ndev)
+{
+	struct ivshm_net *in = netdev_priv(ndev);
+
+	if (!test_and_clear_bit(IVSHM_NET_FLAG_RUN, &in->flags))
+		return;
+
+	netif_stop_queue(ndev);
+	napi_disable(&in->napi);
+}
+
 static void ivshm_net_state_change(struct work_struct *work)
 {
 	struct ivshm_net *in = container_of(work, struct ivshm_net, state_work);
@@ -560,21 +584,13 @@ static void ivshm_net_state_change(struct work_struct *work)
 		break;
 
 	case IVSHM_NET_STATE_READY:
+	case IVSHM_NET_STATE_RUN:
 		if (rstate >= IVSHM_NET_STATE_READY) {
 			netif_carrier_on(ndev);
-			if (ndev->flags & IFF_UP)
-				ivshm_net_run(ndev);
+			ivshm_net_run(ndev);
 		} else {
 			netif_carrier_off(ndev);
-			ivshm_net_set_state(in, IVSHM_NET_STATE_RESET);
-		}
-		break;
-
-	case IVSHM_NET_STATE_RUN:
-		if (rstate < IVSHM_NET_STATE_READY) {
-			netif_stop_queue(ndev);
-			napi_disable(&in->napi);
-			netif_carrier_off(ndev);
+			ivshm_net_do_stop(ndev);
 			ivshm_net_set_state(in, IVSHM_NET_STATE_RESET);
 		}
 		break;
@@ -584,18 +600,13 @@ static void ivshm_net_state_change(struct work_struct *work)
 	WRITE_ONCE(in->rstate, rstate);
 }
 
-static bool ivshm_net_check_state(struct net_device *ndev)
+static void ivshm_net_check_state(struct net_device *ndev)
 {
 	struct ivshm_net *in = netdev_priv(ndev);
 	u32 rstate = readl(&in->ivshm_regs->rstate);
 
-	if (rstate != READ_ONCE(in->rstate) ||
-	    in->lstate != IVSHM_NET_STATE_RUN) {
+	if (rstate != in->rstate || !test_bit(IVSHM_NET_FLAG_RUN, &in->flags))
 		queue_work(in->state_wq, &in->state_work);
-		return false;
-	}
-
-	return true;
 }
 
 static irqreturn_t ivshm_net_int(int irq, void *data)
@@ -617,24 +628,15 @@ static int ivshm_net_open(struct net_device *ndev)
 
 	netdev_reset_queue(ndev);
 	ndev->operstate = IF_OPER_UP;
-
-	if (in->lstate == IVSHM_NET_STATE_READY)
-		ivshm_net_run(ndev);
+	ivshm_net_run(ndev);
 
 	return 0;
 }
 
 static int ivshm_net_stop(struct net_device *ndev)
 {
-	struct ivshm_net *in = netdev_priv(ndev);
-
 	ndev->operstate = IF_OPER_DOWN;
-
-	if (in->lstate == IVSHM_NET_STATE_RUN) {
-		napi_disable(&in->napi);
-		netif_stop_queue(ndev);
-		ivshm_net_set_state(in, IVSHM_NET_STATE_READY);
-	}
+	ivshm_net_do_stop(ndev);
 
 	return 0;
 }
-- 
2.11.0