89f58412d405232f230dcefc6bcf26cef2d555d6
[openwrt/staging/blogic.git] /
1 From 5e448a17dfa2e95166534df7f677a3694ef6187d Mon Sep 17 00:00:00 2001
2 From: Vladimir Oltean <vladimir.oltean@nxp.com>
3 Date: Tue, 29 Nov 2022 16:12:19 +0200
4 Subject: [PATCH 12/14] net: dpaa2-eth: serialize changes to priv->mac with a
5 mutex
6
7 The dpaa2 architecture permits dynamic connections between objects on
8 the fsl-mc bus, specifically between a DPNI object (represented by a
9 struct net_device) and a DPMAC object (represented by a struct phylink).
10
11 The DPNI driver is notified when those connections are created/broken
12 through the dpni_irq0_handler_thread() method. To ensure that ethtool
13 operations, as well as netdev up/down operations serialize with the
14 connection/disconnection of the DPNI with a DPMAC,
15 dpni_irq0_handler_thread() takes the rtnl_lock() to block those other
16 operations from taking place.
17
18 There is code called by dpaa2_mac_connect() which wants to acquire the
19 rtnl_mutex once again, see phylink_create() -> phylink_register_sfp() ->
20 sfp_bus_add_upstream() -> rtnl_lock(). So the strategy doesn't quite
21 work out, even though it's fairly simple.
22
23 Create a different strategy, where all code paths in the dpaa2-eth
24 driver access priv->mac only while they are holding priv->mac_lock.
25 The phylink instance is not created or connected to the PHY under the
26 priv->mac_lock, but only assigned to priv->mac then. This will eliminate
27 the reliance on the rtnl_mutex.
28
29 Add lockdep annotations and put comments where holding the lock is not
30 necessary, and priv->mac can be dereferenced freely.
31
32 Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
33 Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
34 Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
35 Signed-off-by: Paolo Abeni <pabeni@redhat.com>
36 ---
37 .../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 43 ++++++++++++--
38 .../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 6 ++
39 .../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 58 +++++++++++++++----
40 3 files changed, 91 insertions(+), 16 deletions(-)
41
42 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
43 +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
44 @@ -2020,8 +2020,11 @@ static int dpaa2_eth_link_state_update(s
45
46 /* When we manage the MAC/PHY using phylink there is no need
47 * to manually update the netif_carrier.
48 + * We can avoid locking because we are called from the "link changed"
49 + * IRQ handler, which is the same as the "endpoint changed" IRQ handler
50 + * (the writer to priv->mac), so we cannot race with it.
51 */
52 - if (dpaa2_eth_is_type_phy(priv))
53 + if (dpaa2_mac_is_type_phy(priv->mac))
54 goto out;
55
56 /* Chech link state; speed / duplex changes are not treated yet */
57 @@ -2060,6 +2063,8 @@ static int dpaa2_eth_open(struct net_dev
58 priv->dpbp_dev->obj_desc.id, priv->bpid);
59 }
60
61 + mutex_lock(&priv->mac_lock);
62 +
63 if (!dpaa2_eth_is_type_phy(priv)) {
64 /* We'll only start the txqs when the link is actually ready;
65 * make sure we don't race against the link up notification,
66 @@ -2078,6 +2083,7 @@ static int dpaa2_eth_open(struct net_dev
67
68 err = dpni_enable(priv->mc_io, 0, priv->mc_token);
69 if (err < 0) {
70 + mutex_unlock(&priv->mac_lock);
71 netdev_err(net_dev, "dpni_enable() failed\n");
72 goto enable_err;
73 }
74 @@ -2085,6 +2091,8 @@ static int dpaa2_eth_open(struct net_dev
75 if (dpaa2_eth_is_type_phy(priv))
76 dpaa2_mac_start(priv->mac);
77
78 + mutex_unlock(&priv->mac_lock);
79 +
80 return 0;
81
82 enable_err:
83 @@ -2156,6 +2164,8 @@ static int dpaa2_eth_stop(struct net_dev
84 int dpni_enabled = 0;
85 int retries = 10;
86
87 + mutex_lock(&priv->mac_lock);
88 +
89 if (dpaa2_eth_is_type_phy(priv)) {
90 dpaa2_mac_stop(priv->mac);
91 } else {
92 @@ -2163,6 +2173,8 @@ static int dpaa2_eth_stop(struct net_dev
93 netif_carrier_off(net_dev);
94 }
95
96 + mutex_unlock(&priv->mac_lock);
97 +
98 /* On dpni_disable(), the MC firmware will:
99 * - stop MAC Rx and wait for all Rx frames to be enqueued to software
100 * - cut off WRIOP dequeues from egress FQs and wait until transmission
101 @@ -2488,12 +2500,20 @@ static int dpaa2_eth_ts_ioctl(struct net
102 static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
103 {
104 struct dpaa2_eth_priv *priv = netdev_priv(dev);
105 + int err;
106
107 if (cmd == SIOCSHWTSTAMP)
108 return dpaa2_eth_ts_ioctl(dev, rq, cmd);
109
110 - if (dpaa2_eth_is_type_phy(priv))
111 - return phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
112 + mutex_lock(&priv->mac_lock);
113 +
114 + if (dpaa2_eth_is_type_phy(priv)) {
115 + err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
116 + mutex_unlock(&priv->mac_lock);
117 + return err;
118 + }
119 +
120 + mutex_unlock(&priv->mac_lock);
121
122 return -EOPNOTSUPP;
123 }
124 @@ -4453,7 +4473,9 @@ static int dpaa2_eth_connect_mac(struct
125 goto err_close_mac;
126 }
127
128 + mutex_lock(&priv->mac_lock);
129 priv->mac = mac;
130 + mutex_unlock(&priv->mac_lock);
131
132 return 0;
133
134 @@ -4466,9 +4488,12 @@ err_free_mac:
135
136 static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
137 {
138 - struct dpaa2_mac *mac = priv->mac;
139 + struct dpaa2_mac *mac;
140
141 + mutex_lock(&priv->mac_lock);
142 + mac = priv->mac;
143 priv->mac = NULL;
144 + mutex_unlock(&priv->mac_lock);
145
146 if (!mac)
147 return;
148 @@ -4487,6 +4512,7 @@ static irqreturn_t dpni_irq0_handler_thr
149 struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
150 struct net_device *net_dev = dev_get_drvdata(dev);
151 struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
152 + bool had_mac;
153 int err;
154
155 err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
156 @@ -4504,7 +4530,12 @@ static irqreturn_t dpni_irq0_handler_thr
157 dpaa2_eth_update_tx_fqids(priv);
158
159 rtnl_lock();
160 - if (dpaa2_eth_has_mac(priv))
161 + /* We can avoid locking because the "endpoint changed" IRQ
162 + * handler is the only one who changes priv->mac at runtime,
163 + * so we are not racing with anyone.
164 + */
165 + had_mac = !!priv->mac;
166 + if (had_mac)
167 dpaa2_eth_disconnect_mac(priv);
168 else
169 dpaa2_eth_connect_mac(priv);
170 @@ -4605,6 +4636,8 @@ static int dpaa2_eth_probe(struct fsl_mc
171 priv = netdev_priv(net_dev);
172 priv->net_dev = net_dev;
173
174 + mutex_init(&priv->mac_lock);
175 +
176 priv->iommu_domain = iommu_get_domain_for_dev(dev);
177
178 priv->tx_tstamp_type = HWTSTAMP_TX_OFF;
179 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
180 +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
181 @@ -580,6 +580,8 @@ struct dpaa2_eth_priv {
182 #endif
183
184 struct dpaa2_mac *mac;
185 + /* Serializes changes to priv->mac */
186 + struct mutex mac_lock;
187 struct workqueue_struct *dpaa2_ptp_wq;
188 struct work_struct tx_onestep_tstamp;
189 struct sk_buff_head tx_skbs;
190 @@ -733,11 +735,15 @@ static inline unsigned int dpaa2_eth_rx_
191
192 static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv)
193 {
194 + lockdep_assert_held(&priv->mac_lock);
195 +
196 return dpaa2_mac_is_type_phy(priv->mac);
197 }
198
199 static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
200 {
201 + lockdep_assert_held(&priv->mac_lock);
202 +
203 return priv->mac ? true : false;
204 }
205
206 --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
207 +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
208 @@ -86,11 +86,16 @@ static void dpaa2_eth_get_drvinfo(struct
209 static int dpaa2_eth_nway_reset(struct net_device *net_dev)
210 {
211 struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
212 + int err = -EOPNOTSUPP;
213 +
214 + mutex_lock(&priv->mac_lock);
215
216 if (dpaa2_eth_is_type_phy(priv))
217 - return phylink_ethtool_nway_reset(priv->mac->phylink);
218 + err = phylink_ethtool_nway_reset(priv->mac->phylink);
219 +
220 + mutex_unlock(&priv->mac_lock);
221
222 - return -EOPNOTSUPP;
223 + return err;
224 }
225
226 static int
227 @@ -98,10 +103,18 @@ dpaa2_eth_get_link_ksettings(struct net_
228 struct ethtool_link_ksettings *link_settings)
229 {
230 struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
231 + int err;
232
233 - if (dpaa2_eth_is_type_phy(priv))
234 - return phylink_ethtool_ksettings_get(priv->mac->phylink,
235 - link_settings);
236 + mutex_lock(&priv->mac_lock);
237 +
238 + if (dpaa2_eth_is_type_phy(priv)) {
239 + err = phylink_ethtool_ksettings_get(priv->mac->phylink,
240 + link_settings);
241 + mutex_unlock(&priv->mac_lock);
242 + return err;
243 + }
244 +
245 + mutex_unlock(&priv->mac_lock);
246
247 link_settings->base.autoneg = AUTONEG_DISABLE;
248 if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX))
249 @@ -116,11 +129,17 @@ dpaa2_eth_set_link_ksettings(struct net_
250 const struct ethtool_link_ksettings *link_settings)
251 {
252 struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
253 + int err = -EOPNOTSUPP;
254
255 - if (!dpaa2_eth_is_type_phy(priv))
256 - return -EOPNOTSUPP;
257 + mutex_lock(&priv->mac_lock);
258 +
259 + if (dpaa2_eth_is_type_phy(priv))
260 + err = phylink_ethtool_ksettings_set(priv->mac->phylink,
261 + link_settings);
262
263 - return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
264 + mutex_unlock(&priv->mac_lock);
265 +
266 + return err;
267 }
268
269 static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
270 @@ -129,11 +148,16 @@ static void dpaa2_eth_get_pauseparam(str
271 struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
272 u64 link_options = priv->link_state.options;
273
274 + mutex_lock(&priv->mac_lock);
275 +
276 if (dpaa2_eth_is_type_phy(priv)) {
277 phylink_ethtool_get_pauseparam(priv->mac->phylink, pause);
278 + mutex_unlock(&priv->mac_lock);
279 return;
280 }
281
282 + mutex_unlock(&priv->mac_lock);
283 +
284 pause->rx_pause = dpaa2_eth_rx_pause_enabled(link_options);
285 pause->tx_pause = dpaa2_eth_tx_pause_enabled(link_options);
286 pause->autoneg = AUTONEG_DISABLE;
287 @@ -152,9 +176,17 @@ static int dpaa2_eth_set_pauseparam(stru
288 return -EOPNOTSUPP;
289 }
290
291 - if (dpaa2_eth_is_type_phy(priv))
292 - return phylink_ethtool_set_pauseparam(priv->mac->phylink,
293 - pause);
294 + mutex_lock(&priv->mac_lock);
295 +
296 + if (dpaa2_eth_is_type_phy(priv)) {
297 + err = phylink_ethtool_set_pauseparam(priv->mac->phylink,
298 + pause);
299 + mutex_unlock(&priv->mac_lock);
300 + return err;
301 + }
302 +
303 + mutex_unlock(&priv->mac_lock);
304 +
305 if (pause->autoneg)
306 return -EOPNOTSUPP;
307
308 @@ -309,8 +341,12 @@ static void dpaa2_eth_get_ethtool_stats(
309 }
310 *(data + i++) = buf_cnt;
311
312 + mutex_lock(&priv->mac_lock);
313 +
314 if (dpaa2_eth_has_mac(priv))
315 dpaa2_mac_get_ethtool_stats(priv->mac, data + i);
316 +
317 + mutex_unlock(&priv->mac_lock);
318 }
319
320 static int dpaa2_eth_prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,