summaryrefslogtreecommitdiffstats
path: root/drivers/net/usb
diff options
context:
space:
mode:
authorReinhard Speyerer <rspmn@arcor.de>2019-06-12 13:03:15 -0400
committerDavid S. Miller <davem@davemloft.net>2019-06-14 22:05:58 -0400
commita8fdde1cb830e560208af42b6c10750137f53eb3 (patch)
tree6d0331ae62daba32a17f21fbed2892f6de93b7b1 /drivers/net/usb
parent44f82312fe9113bab6642f4d0eab6b1b7902b6e1 (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.c11
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
315static void qmimux_unregister_device(struct net_device *dev) 315static 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 }