[ Upstream commit a8fdde1cb830e560208af42b6c10750137f53eb3 ]

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 <dnl...@gmail.com>
Signed-off-by: Reinhard Speyerer <rs...@arcor.de>
Signed-off-by: David S. Miller <da...@davemloft.net>
Signed-off-by: Sasha Levin <sas...@kernel.org>
---
 drivers/net/usb/qmi_wwan.c | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 75fe5c5abec4..76c4afac71f7 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -247,13 +247,14 @@ static int qmimux_register_device(struct net_device 
*real_dev, u8 mux_id)
        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;
 
        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);
@@ -424,7 +425,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;
@@ -1423,6 +1424,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)
@@ -1435,8 +1437,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;
        }
-- 
2.20.1



Reply via email to