diff options
author | Reinhard Speyerer <rspmn@arcor.de> | 2019-06-12 13:03:15 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-06-14 22:05:58 -0400 |
commit | a8fdde1cb830e560208af42b6c10750137f53eb3 (patch) | |
tree | 6d0331ae62daba32a17f21fbed2892f6de93b7b1 /drivers/net/usb | |
parent | 44f82312fe9113bab6642f4d0eab6b1b7902b6e1 (diff) |
qmi_wwan: avoid RCU stalls on device disconnect when in QMAP mode
Switch qmimux_unregister_device() and qmi_wwan_disconnect() to
use unregister_netdevice_queue() and unregister_netdevice_many()
instead of unregister_netdevice(). This avoids RCU stalls which
have been observed on device disconnect in certain setups otherwise.
Fixes: c6adf77953bc ("net: usb: qmi_wwan: add qmap mux protocol support")
Cc: Daniele Palmas <dnlplm@gmail.com>
Signed-off-by: Reinhard Speyerer <rspmn@arcor.de>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 11 |
1 files changed, 7 insertions, 4 deletions
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index b0a96459621f..c6fbc2a2a785 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c | |||
@@ -312,14 +312,15 @@ out_free_newdev: | |||
312 | return err; | 312 | return err; |
313 | } | 313 | } |
314 | 314 | ||
315 | static void qmimux_unregister_device(struct net_device *dev) | 315 | static void qmimux_unregister_device(struct net_device *dev, |
316 | struct list_head *head) | ||
316 | { | 317 | { |
317 | struct qmimux_priv *priv = netdev_priv(dev); | 318 | struct qmimux_priv *priv = netdev_priv(dev); |
318 | struct net_device *real_dev = priv->real_dev; | 319 | struct net_device *real_dev = priv->real_dev; |
319 | 320 | ||
320 | free_percpu(priv->stats64); | 321 | free_percpu(priv->stats64); |
321 | netdev_upper_dev_unlink(real_dev, dev); | 322 | netdev_upper_dev_unlink(real_dev, dev); |
322 | unregister_netdevice(dev); | 323 | unregister_netdevice_queue(dev, head); |
323 | 324 | ||
324 | /* Get rid of the reference to real_dev */ | 325 | /* Get rid of the reference to real_dev */ |
325 | dev_put(real_dev); | 326 | dev_put(real_dev); |
@@ -490,7 +491,7 @@ static ssize_t del_mux_store(struct device *d, struct device_attribute *attr, c | |||
490 | ret = -EINVAL; | 491 | ret = -EINVAL; |
491 | goto err; | 492 | goto err; |
492 | } | 493 | } |
493 | qmimux_unregister_device(del_dev); | 494 | qmimux_unregister_device(del_dev, NULL); |
494 | 495 | ||
495 | if (!qmimux_has_slaves(dev)) | 496 | if (!qmimux_has_slaves(dev)) |
496 | info->flags &= ~QMI_WWAN_FLAG_MUX; | 497 | info->flags &= ~QMI_WWAN_FLAG_MUX; |
@@ -1500,6 +1501,7 @@ static void qmi_wwan_disconnect(struct usb_interface *intf) | |||
1500 | struct qmi_wwan_state *info; | 1501 | struct qmi_wwan_state *info; |
1501 | struct list_head *iter; | 1502 | struct list_head *iter; |
1502 | struct net_device *ldev; | 1503 | struct net_device *ldev; |
1504 | LIST_HEAD(list); | ||
1503 | 1505 | ||
1504 | /* called twice if separate control and data intf */ | 1506 | /* called twice if separate control and data intf */ |
1505 | if (!dev) | 1507 | if (!dev) |
@@ -1512,8 +1514,9 @@ static void qmi_wwan_disconnect(struct usb_interface *intf) | |||
1512 | } | 1514 | } |
1513 | rcu_read_lock(); | 1515 | rcu_read_lock(); |
1514 | netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) | 1516 | netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) |
1515 | qmimux_unregister_device(ldev); | 1517 | qmimux_unregister_device(ldev, &list); |
1516 | rcu_read_unlock(); | 1518 | rcu_read_unlock(); |
1519 | unregister_netdevice_many(&list); | ||
1517 | rtnl_unlock(); | 1520 | rtnl_unlock(); |
1518 | info->flags &= ~QMI_WWAN_FLAG_MUX; | 1521 | info->flags &= ~QMI_WWAN_FLAG_MUX; |
1519 | } | 1522 | } |