diff options
author | Reinhard Speyerer <rspmn@arcor.de> | 2019-06-12 19:03:15 +0200 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2019-06-14 19:05:58 -0700 |
commit | a8fdde1cb830e560208af42b6c10750137f53eb3 (patch) | |
tree | 6d0331ae62daba32a17f21fbed2892f6de93b7b1 | |
parent | 44f82312fe9113bab6642f4d0eab6b1b7902b6e1 (diff) | |
download | linux-a8fdde1cb830e560208af42b6c10750137f53eb3.tar.gz linux-a8fdde1cb830e560208af42b6c10750137f53eb3.tar.bz2 linux-a8fdde1cb830e560208af42b6c10750137f53eb3.zip |
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>
-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: return err; } -static void qmimux_unregister_device(struct net_device *dev) +static void qmimux_unregister_device(struct net_device *dev, + struct list_head *head) { struct qmimux_priv *priv = netdev_priv(dev); struct net_device *real_dev = priv->real_dev; free_percpu(priv->stats64); netdev_upper_dev_unlink(real_dev, dev); - unregister_netdevice(dev); + unregister_netdevice_queue(dev, head); /* Get rid of the reference to real_dev */ dev_put(real_dev); @@ -490,7 +491,7 @@ static ssize_t del_mux_store(struct device *d, struct device_attribute *attr, c ret = -EINVAL; goto err; } - qmimux_unregister_device(del_dev); + qmimux_unregister_device(del_dev, NULL); if (!qmimux_has_slaves(dev)) info->flags &= ~QMI_WWAN_FLAG_MUX; @@ -1500,6 +1501,7 @@ static void qmi_wwan_disconnect(struct usb_interface *intf) struct qmi_wwan_state *info; struct list_head *iter; struct net_device *ldev; + LIST_HEAD(list); /* called twice if separate control and data intf */ if (!dev) @@ -1512,8 +1514,9 @@ static void qmi_wwan_disconnect(struct usb_interface *intf) } rcu_read_lock(); netdev_for_each_upper_dev_rcu(dev->net, ldev, iter) - qmimux_unregister_device(ldev); + qmimux_unregister_device(ldev, &list); rcu_read_unlock(); + unregister_netdevice_many(&list); rtnl_unlock(); info->flags &= ~QMI_WWAN_FLAG_MUX; } |