kernel: bump 5.4 to 5.4.109
[openwrt/openwrt.git] / target / linux / layerscape / patches-5.4 / 809-jailhouse-0003-ivshmem-net-fix-race-in-state-machine.patch
1 From 0af5d7d021bb899d9c3880415267e178a20fb7a9 Mon Sep 17 00:00:00 2001
2 From: Mans Rullgard <mans@mansr.com>
3 Date: Thu, 24 Nov 2016 18:46:41 +0000
4 Subject: [PATCH] ivshmem-net: fix race in state machine
5
6 (cherry picked from commit 5d663baed6a89d09bae4446f6509f9957c780bc7)
7 ---
8 drivers/net/ivshmem-net.c | 60 ++++++++++++++++++++++++-----------------------
9 1 file changed, 31 insertions(+), 29 deletions(-)
10
11 --- a/drivers/net/ivshmem-net.c
12 +++ b/drivers/net/ivshmem-net.c
13 @@ -36,6 +36,8 @@
14 #define IVSHM_NET_STATE_READY 2
15 #define IVSHM_NET_STATE_RUN 3
16
17 +#define IVSHM_NET_FLAG_RUN 0
18 +
19 #define IVSHM_NET_MTU_MIN 256
20 #define IVSHM_NET_MTU_MAX 65535
21 #define IVSHM_NET_MTU_DEF 16384
22 @@ -96,6 +98,8 @@ struct ivshm_net {
23 u32 lstate;
24 u32 rstate;
25
26 + unsigned long flags;
27 +
28 struct workqueue_struct *state_wq;
29 struct work_struct state_work;
30
31 @@ -529,12 +533,32 @@ static void ivshm_net_run(struct net_dev
32 {
33 struct ivshm_net *in = netdev_priv(ndev);
34
35 + if (in->lstate < IVSHM_NET_STATE_READY)
36 + return;
37 +
38 + if (!netif_running(ndev))
39 + return;
40 +
41 + if (test_and_set_bit(IVSHM_NET_FLAG_RUN, &in->flags))
42 + return;
43 +
44 netif_start_queue(ndev);
45 napi_enable(&in->napi);
46 napi_schedule(&in->napi);
47 ivshm_net_set_state(in, IVSHM_NET_STATE_RUN);
48 }
49
50 +static void ivshm_net_do_stop(struct net_device *ndev)
51 +{
52 + struct ivshm_net *in = netdev_priv(ndev);
53 +
54 + if (!test_and_clear_bit(IVSHM_NET_FLAG_RUN, &in->flags))
55 + return;
56 +
57 + netif_stop_queue(ndev);
58 + napi_disable(&in->napi);
59 +}
60 +
61 static void ivshm_net_state_change(struct work_struct *work)
62 {
63 struct ivshm_net *in = container_of(work, struct ivshm_net, state_work);
64 @@ -560,21 +584,13 @@ static void ivshm_net_state_change(struc
65 break;
66
67 case IVSHM_NET_STATE_READY:
68 + case IVSHM_NET_STATE_RUN:
69 if (rstate >= IVSHM_NET_STATE_READY) {
70 netif_carrier_on(ndev);
71 - if (ndev->flags & IFF_UP)
72 - ivshm_net_run(ndev);
73 + ivshm_net_run(ndev);
74 } else {
75 netif_carrier_off(ndev);
76 - ivshm_net_set_state(in, IVSHM_NET_STATE_RESET);
77 - }
78 - break;
79 -
80 - case IVSHM_NET_STATE_RUN:
81 - if (rstate < IVSHM_NET_STATE_READY) {
82 - netif_stop_queue(ndev);
83 - napi_disable(&in->napi);
84 - netif_carrier_off(ndev);
85 + ivshm_net_do_stop(ndev);
86 ivshm_net_set_state(in, IVSHM_NET_STATE_RESET);
87 }
88 break;
89 @@ -584,18 +600,13 @@ static void ivshm_net_state_change(struc
90 WRITE_ONCE(in->rstate, rstate);
91 }
92
93 -static bool ivshm_net_check_state(struct net_device *ndev)
94 +static void ivshm_net_check_state(struct net_device *ndev)
95 {
96 struct ivshm_net *in = netdev_priv(ndev);
97 u32 rstate = readl(&in->ivshm_regs->rstate);
98
99 - if (rstate != READ_ONCE(in->rstate) ||
100 - in->lstate != IVSHM_NET_STATE_RUN) {
101 + if (rstate != in->rstate || !test_bit(IVSHM_NET_FLAG_RUN, &in->flags))
102 queue_work(in->state_wq, &in->state_work);
103 - return false;
104 - }
105 -
106 - return true;
107 }
108
109 static irqreturn_t ivshm_net_int(int irq, void *data)
110 @@ -617,24 +628,15 @@ static int ivshm_net_open(struct net_dev
111
112 netdev_reset_queue(ndev);
113 ndev->operstate = IF_OPER_UP;
114 -
115 - if (in->lstate == IVSHM_NET_STATE_READY)
116 - ivshm_net_run(ndev);
117 + ivshm_net_run(ndev);
118
119 return 0;
120 }
121
122 static int ivshm_net_stop(struct net_device *ndev)
123 {
124 - struct ivshm_net *in = netdev_priv(ndev);
125 -
126 ndev->operstate = IF_OPER_DOWN;
127 -
128 - if (in->lstate == IVSHM_NET_STATE_RUN) {
129 - napi_disable(&in->napi);
130 - netif_stop_queue(ndev);
131 - ivshm_net_set_state(in, IVSHM_NET_STATE_READY);
132 - }
133 + ivshm_net_do_stop(ndev);
134
135 return 0;
136 }