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
|
From 0af5d7d021bb899d9c3880415267e178a20fb7a9 Mon Sep 17 00:00:00 2001
From: Mans Rullgard <mans@mansr.com>
Date: Thu, 24 Nov 2016 18:46:41 +0000
Subject: [PATCH] ivshmem-net: fix race in state machine
(cherry picked from commit 5d663baed6a89d09bae4446f6509f9957c780bc7)
---
drivers/net/ivshmem-net.c | 60 ++++++++++++++++++++++++-----------------------
1 file changed, 31 insertions(+), 29 deletions(-)
--- 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_dev
{
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(struc
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(struc
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_dev
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;
}
|