--- a/drivers/staging/rtl8712/drv_types.h
+++ b/drivers/staging/rtl8712/drv_types.h
@@ -163,6 +163,7 @@ struct _adapter {
struct usb_interface *pusb_intf;
struct mutex mutex_start;
struct completion rtl8712_fw_ready;
+ atomic_t disconnecting;
};
static inline u8 *myid(struct eeprom_priv *peepriv)
--- a/drivers/staging/rtl8712/usb_intf.c
+++ b/drivers/staging/rtl8712/usb_intf.c
@@ -589,15 +589,29 @@ put_dev:
*/
static void r871xu_dev_remove(struct usb_interface *pusb_intf)
{
- struct net_device *pnetdev = usb_get_intfdata(pusb_intf);
- struct usb_device *udev = interface_to_usbdev(pusb_intf);
- struct _adapter *padapter = netdev_priv(pnetdev);
+ struct net_device *pnetdev;
+ struct usb_device *udev;
+ struct _adapter *padapter = NULL;
+
+ rcu_read_lock();
+ pnetdev = usb_get_intfdata(pusb_intf);
+ if (pnetdev) {
+ padapter = netdev_priv(pnetdev);
+ if (1 == atomic_inc_return(&padapter->disconnecting))
+ usb_set_intfdata(pusb_intf, NULL);
+ else
+ padapter = NULL;
+ }
+ rcu_read_unlock();
+ if (padapter == NULL)
+ return;
+ synchronize_rcu();
+ udev = interface_to_usbdev(pusb_intf);
/* never exit with a firmware callback pending */
wait_for_completion(&padapter->rtl8712_fw_ready);
if (pnetdev->reg_state != NETREG_UNINITIALIZED)
unregister_netdev(pnetdev); /* will call netdev_close() */
- usb_set_intfdata(pusb_intf, NULL);
release_firmware(padapter->fw);
if (drvpriv.drv_registered)
padapter->surprise_removed = true;