Skip to content

Commit 9e855b1

Browse files
author
Paolo Abeni
committed
Merge branch 'fix-rtnl_mutex-deadlock-with-dpaa2-and-sfp-modules'
Vladimir Oltean says: ==================== Fix rtnl_mutex deadlock with DPAA2 and SFP modules This patch set deliberately targets net-next and lacks Fixes: tags due to caution on my part. While testing some SFP modules on the Solidrun Honeycomb LX2K platform, I noticed that rebooting causes a deadlock: ============================================ WARNING: possible recursive locking detected 6.1.0-rc5-07010-ga9b9500ffaac-dirty #656 Not tainted -------------------------------------------- systemd-shutdow/1 is trying to acquire lock: ffffa62db6cf42f0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock+0x1c/0x30 but task is already holding lock: ffffa62db6cf42f0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock+0x1c/0x30 other info that might help us debug this: Possible unsafe locking scenario: CPU0 ---- lock(rtnl_mutex); lock(rtnl_mutex); *** DEADLOCK *** May be due to missing lock nesting notation 6 locks held by systemd-shutdow/1: #0: ffffa62db6863c70 (system_transition_mutex){+.+.}-{4:4}, at: __do_sys_reboot+0xd4/0x260 #1: ffff2f2b0176f100 (&dev->mutex){....}-{4:4}, at: device_shutdown+0xf4/0x260 #2: ffff2f2b017be900 (&dev->mutex){....}-{4:4}, at: device_shutdown+0x104/0x260 #3: ffff2f2b017680f0 (&dev->mutex){....}-{4:4}, at: device_release_driver_internal+0x40/0x260 #4: ffff2f2b0e1608f0 (&dev->mutex){....}-{4:4}, at: device_release_driver_internal+0x40/0x260 #5: ffffa62db6cf42f0 (rtnl_mutex){+.+.}-{4:4}, at: rtnl_lock+0x1c/0x30 stack backtrace: CPU: 6 PID: 1 Comm: systemd-shutdow Not tainted 6.1.0-rc5-07010-ga9b9500ffaac-dirty #656 Hardware name: SolidRun LX2160A Honeycomb (DT) Call trace: lock_acquire+0x68/0x84 __mutex_lock+0x98/0x460 mutex_lock_nested+0x2c/0x40 rtnl_lock+0x1c/0x30 sfp_bus_del_upstream+0x1c/0xac phylink_destroy+0x1c/0x50 dpaa2_mac_disconnect+0x28/0x70 dpaa2_eth_remove+0x1dc/0x1f0 fsl_mc_driver_remove+0x24/0x60 device_remove+0x70/0x80 device_release_driver_internal+0x1f0/0x260 device_links_unbind_consumers+0xe0/0x110 device_release_driver_internal+0x138/0x260 device_release_driver+0x18/0x24 bus_remove_device+0x12c/0x13c device_del+0x16c/0x424 fsl_mc_device_remove+0x28/0x40 __fsl_mc_device_remove+0x10/0x20 device_for_each_child+0x5c/0xac dprc_remove+0x94/0xb4 fsl_mc_driver_remove+0x24/0x60 device_remove+0x70/0x80 device_release_driver_internal+0x1f0/0x260 device_release_driver+0x18/0x24 bus_remove_device+0x12c/0x13c device_del+0x16c/0x424 fsl_mc_bus_remove+0x8c/0x10c fsl_mc_bus_shutdown+0x10/0x20 platform_shutdown+0x24/0x3c device_shutdown+0x15c/0x260 kernel_restart+0x40/0xa4 __do_sys_reboot+0x1e4/0x260 __arm64_sys_reboot+0x24/0x30 But fixing this appears to be not so simple. The patch set represents my attempt to address it. In short, the problem is that dpaa2_mac_connect() and dpaa2_mac_disconnect() call 2 phylink functions in a row, one takes rtnl_lock() itself - phylink_create(), and one which requires rtnl_lock() to be held by the caller - phylink_fwnode_phy_connect(). The existing approach in the drivers is too simple. We take rtnl_lock() when calling dpaa2_mac_connect(), which is what results in the deadlock. Fixing just that creates another problem. The drivers make use of rtnl_lock() for serializing with other code paths too. I think I've found all those code paths, and established other mechanisms for serializing with them. ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Paolo Abeni <[email protected]>
2 parents 682f560 + 87db82c commit 9e855b1

File tree

9 files changed

+212
-104
lines changed

9 files changed

+212
-104
lines changed

Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,13 @@ when necessary using the below listed API::
181181
- int dpaa2_mac_connect(struct dpaa2_mac *mac);
182182
- void dpaa2_mac_disconnect(struct dpaa2_mac *mac);
183183

184-
A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED.
185-
One can check for this condition using the below API::
184+
A phylink integration is necessary only when the partner DPMAC is not of
185+
``TYPE_FIXED``. This means it is either of ``TYPE_PHY``, or of
186+
``TYPE_BACKPLANE`` (the difference being the two that in the ``TYPE_BACKPLANE``
187+
mode, the MC firmware does not access the PCS registers). One can check for
188+
this condition using the following helper::
186189

187-
- bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io);
190+
- static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac);
188191

189192
Before connection to a MAC, the caller must allocate and populate the
190193
dpaa2_mac structure with the associated net_device, a pointer to the MC portal

drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2147,8 +2147,11 @@ static int dpaa2_eth_link_state_update(struct dpaa2_eth_priv *priv)
21472147

21482148
/* When we manage the MAC/PHY using phylink there is no need
21492149
* to manually update the netif_carrier.
2150+
* We can avoid locking because we are called from the "link changed"
2151+
* IRQ handler, which is the same as the "endpoint changed" IRQ handler
2152+
* (the writer to priv->mac), so we cannot race with it.
21502153
*/
2151-
if (dpaa2_eth_is_type_phy(priv))
2154+
if (dpaa2_mac_is_type_phy(priv->mac))
21522155
goto out;
21532156

21542157
/* Chech link state; speed / duplex changes are not treated yet */
@@ -2179,6 +2182,8 @@ static int dpaa2_eth_open(struct net_device *net_dev)
21792182

21802183
dpaa2_eth_seed_pools(priv);
21812184

2185+
mutex_lock(&priv->mac_lock);
2186+
21822187
if (!dpaa2_eth_is_type_phy(priv)) {
21832188
/* We'll only start the txqs when the link is actually ready;
21842189
* make sure we don't race against the link up notification,
@@ -2197,14 +2202,15 @@ static int dpaa2_eth_open(struct net_device *net_dev)
21972202

21982203
err = dpni_enable(priv->mc_io, 0, priv->mc_token);
21992204
if (err < 0) {
2205+
mutex_unlock(&priv->mac_lock);
22002206
netdev_err(net_dev, "dpni_enable() failed\n");
22012207
goto enable_err;
22022208
}
22032209

2204-
if (dpaa2_eth_is_type_phy(priv)) {
2210+
if (dpaa2_eth_is_type_phy(priv))
22052211
dpaa2_mac_start(priv->mac);
2206-
phylink_start(priv->mac->phylink);
2207-
}
2212+
2213+
mutex_unlock(&priv->mac_lock);
22082214

22092215
return 0;
22102216

@@ -2277,14 +2283,17 @@ static int dpaa2_eth_stop(struct net_device *net_dev)
22772283
int dpni_enabled = 0;
22782284
int retries = 10;
22792285

2286+
mutex_lock(&priv->mac_lock);
2287+
22802288
if (dpaa2_eth_is_type_phy(priv)) {
2281-
phylink_stop(priv->mac->phylink);
22822289
dpaa2_mac_stop(priv->mac);
22832290
} else {
22842291
netif_tx_stop_all_queues(net_dev);
22852292
netif_carrier_off(net_dev);
22862293
}
22872294

2295+
mutex_unlock(&priv->mac_lock);
2296+
22882297
/* On dpni_disable(), the MC firmware will:
22892298
* - stop MAC Rx and wait for all Rx frames to be enqueued to software
22902299
* - cut off WRIOP dequeues from egress FQs and wait until transmission
@@ -2610,12 +2619,20 @@ static int dpaa2_eth_ts_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
26102619
static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
26112620
{
26122621
struct dpaa2_eth_priv *priv = netdev_priv(dev);
2622+
int err;
26132623

26142624
if (cmd == SIOCSHWTSTAMP)
26152625
return dpaa2_eth_ts_ioctl(dev, rq, cmd);
26162626

2617-
if (dpaa2_eth_is_type_phy(priv))
2618-
return phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
2627+
mutex_lock(&priv->mac_lock);
2628+
2629+
if (dpaa2_eth_is_type_phy(priv)) {
2630+
err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
2631+
mutex_unlock(&priv->mac_lock);
2632+
return err;
2633+
}
2634+
2635+
mutex_unlock(&priv->mac_lock);
26192636

26202637
return -EOPNOTSUPP;
26212638
}
@@ -3791,7 +3808,7 @@ static int dpaa2_eth_setup_dpni(struct fsl_mc_device *ls_dev)
37913808
dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n",
37923809
priv->dpni_ver_major, priv->dpni_ver_minor,
37933810
DPNI_VER_MAJOR, DPNI_VER_MINOR);
3794-
err = -ENOTSUPP;
3811+
err = -EOPNOTSUPP;
37953812
goto close;
37963813
}
37973814

@@ -4627,9 +4644,8 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
46274644
err = dpaa2_mac_open(mac);
46284645
if (err)
46294646
goto err_free_mac;
4630-
priv->mac = mac;
46314647

4632-
if (dpaa2_eth_is_type_phy(priv)) {
4648+
if (dpaa2_mac_is_type_phy(mac)) {
46334649
err = dpaa2_mac_connect(mac);
46344650
if (err) {
46354651
if (err == -EPROBE_DEFER)
@@ -4643,27 +4659,36 @@ static int dpaa2_eth_connect_mac(struct dpaa2_eth_priv *priv)
46434659
}
46444660
}
46454661

4662+
mutex_lock(&priv->mac_lock);
4663+
priv->mac = mac;
4664+
mutex_unlock(&priv->mac_lock);
4665+
46464666
return 0;
46474667

46484668
err_close_mac:
46494669
dpaa2_mac_close(mac);
4650-
priv->mac = NULL;
46514670
err_free_mac:
46524671
kfree(mac);
46534672
return err;
46544673
}
46554674

46564675
static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
46574676
{
4658-
if (dpaa2_eth_is_type_phy(priv))
4659-
dpaa2_mac_disconnect(priv->mac);
4677+
struct dpaa2_mac *mac;
46604678

4661-
if (!dpaa2_eth_has_mac(priv))
4679+
mutex_lock(&priv->mac_lock);
4680+
mac = priv->mac;
4681+
priv->mac = NULL;
4682+
mutex_unlock(&priv->mac_lock);
4683+
4684+
if (!mac)
46624685
return;
46634686

4664-
dpaa2_mac_close(priv->mac);
4665-
kfree(priv->mac);
4666-
priv->mac = NULL;
4687+
if (dpaa2_mac_is_type_phy(mac))
4688+
dpaa2_mac_disconnect(mac);
4689+
4690+
dpaa2_mac_close(mac);
4691+
kfree(mac);
46674692
}
46684693

46694694
static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
@@ -4673,6 +4698,7 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
46734698
struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
46744699
struct net_device *net_dev = dev_get_drvdata(dev);
46754700
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
4701+
bool had_mac;
46764702
int err;
46774703

46784704
err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
@@ -4689,12 +4715,15 @@ static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
46894715
dpaa2_eth_set_mac_addr(netdev_priv(net_dev));
46904716
dpaa2_eth_update_tx_fqids(priv);
46914717

4692-
rtnl_lock();
4693-
if (dpaa2_eth_has_mac(priv))
4718+
/* We can avoid locking because the "endpoint changed" IRQ
4719+
* handler is the only one who changes priv->mac at runtime,
4720+
* so we are not racing with anyone.
4721+
*/
4722+
had_mac = !!priv->mac;
4723+
if (had_mac)
46944724
dpaa2_eth_disconnect_mac(priv);
46954725
else
46964726
dpaa2_eth_connect_mac(priv);
4697-
rtnl_unlock();
46984727
}
46994728

47004729
return IRQ_HANDLED;
@@ -4792,6 +4821,8 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
47924821
priv->net_dev = net_dev;
47934822
SET_NETDEV_DEVLINK_PORT(net_dev, &priv->devlink_port);
47944823

4824+
mutex_init(&priv->mac_lock);
4825+
47954826
priv->iommu_domain = iommu_get_domain_for_dev(dev);
47964827

47974828
priv->tx_tstamp_type = HWTSTAMP_TX_OFF;
@@ -4899,6 +4930,10 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
48994930
}
49004931
#endif
49014932

4933+
err = dpaa2_eth_connect_mac(priv);
4934+
if (err)
4935+
goto err_connect_mac;
4936+
49024937
err = dpaa2_eth_setup_irqs(dpni_dev);
49034938
if (err) {
49044939
netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n");
@@ -4911,10 +4946,6 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
49114946
priv->do_link_poll = true;
49124947
}
49134948

4914-
err = dpaa2_eth_connect_mac(priv);
4915-
if (err)
4916-
goto err_connect_mac;
4917-
49184949
err = dpaa2_eth_dl_alloc(priv);
49194950
if (err)
49204951
goto err_dl_register;
@@ -4948,13 +4979,13 @@ static int dpaa2_eth_probe(struct fsl_mc_device *dpni_dev)
49484979
err_dl_trap_register:
49494980
dpaa2_eth_dl_free(priv);
49504981
err_dl_register:
4951-
dpaa2_eth_disconnect_mac(priv);
4952-
err_connect_mac:
49534982
if (priv->do_link_poll)
49544983
kthread_stop(priv->poll_thread);
49554984
else
49564985
fsl_mc_free_irqs(dpni_dev);
49574986
err_poll_thread:
4987+
dpaa2_eth_disconnect_mac(priv);
4988+
err_connect_mac:
49584989
dpaa2_eth_free_rings(priv);
49594990
err_alloc_rings:
49604991
err_csum:
@@ -5002,9 +5033,6 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
50025033
#endif
50035034

50045035
unregister_netdev(net_dev);
5005-
rtnl_lock();
5006-
dpaa2_eth_disconnect_mac(priv);
5007-
rtnl_unlock();
50085036

50095037
dpaa2_eth_dl_port_del(priv);
50105038
dpaa2_eth_dl_traps_unregister(priv);
@@ -5015,6 +5043,7 @@ static int dpaa2_eth_remove(struct fsl_mc_device *ls_dev)
50155043
else
50165044
fsl_mc_free_irqs(ls_dev);
50175045

5046+
dpaa2_eth_disconnect_mac(priv);
50185047
dpaa2_eth_free_rings(priv);
50195048
free_percpu(priv->fd);
50205049
free_percpu(priv->sgt_cache);

drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,8 @@ struct dpaa2_eth_priv {
615615
#endif
616616

617617
struct dpaa2_mac *mac;
618+
/* Serializes changes to priv->mac */
619+
struct mutex mac_lock;
618620
struct workqueue_struct *dpaa2_ptp_wq;
619621
struct work_struct tx_onestep_tstamp;
620622
struct sk_buff_head tx_skbs;
@@ -768,16 +770,15 @@ static inline unsigned int dpaa2_eth_rx_head_room(struct dpaa2_eth_priv *priv)
768770

769771
static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv)
770772
{
771-
if (priv->mac &&
772-
(priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
773-
priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
774-
return true;
773+
lockdep_assert_held(&priv->mac_lock);
775774

776-
return false;
775+
return dpaa2_mac_is_type_phy(priv->mac);
777776
}
778777

779778
static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
780779
{
780+
lockdep_assert_held(&priv->mac_lock);
781+
781782
return priv->mac ? true : false;
782783
}
783784

0 commit comments

Comments
 (0)