--- 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;