|
@@ -430,6 +430,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)
|
|
|
INIT_WORK(&rdev->conn_work, cfg80211_conn_work);
|
|
|
INIT_WORK(&rdev->event_work, cfg80211_event_work);
|
|
|
|
|
|
+ init_waitqueue_head(&rdev->dev_wait);
|
|
|
+
|
|
|
/*
|
|
|
* Initialize wiphy parameters to IEEE 802.11 MIB default values.
|
|
|
* Fragmentation and RTS threshold are disabled by default with the
|
|
@@ -574,7 +576,23 @@ void wiphy_unregister(struct wiphy *wiphy)
|
|
|
/* protect the device list */
|
|
|
mutex_lock(&cfg80211_mutex);
|
|
|
|
|
|
+ wait_event(rdev->dev_wait, ({
|
|
|
+ int __count;
|
|
|
+ mutex_lock(&rdev->devlist_mtx);
|
|
|
+ __count = rdev->opencount;
|
|
|
+ mutex_unlock(&rdev->devlist_mtx);
|
|
|
+ __count == 0;}));
|
|
|
+
|
|
|
+ mutex_lock(&rdev->devlist_mtx);
|
|
|
BUG_ON(!list_empty(&rdev->netdev_list));
|
|
|
+ mutex_unlock(&rdev->devlist_mtx);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * First remove the hardware from everywhere, this makes
|
|
|
+ * it impossible to find from userspace.
|
|
|
+ */
|
|
|
+ cfg80211_debugfs_rdev_del(rdev);
|
|
|
+ list_del(&rdev->list);
|
|
|
|
|
|
/*
|
|
|
* Try to grab rdev->mtx. If a command is still in progress,
|
|
@@ -582,26 +600,18 @@ void wiphy_unregister(struct wiphy *wiphy)
|
|
|
* down the device already. We wait for this command to complete
|
|
|
* before unlinking the item from the list.
|
|
|
* Note: as codified by the BUG_ON above we cannot get here if
|
|
|
- * a virtual interface is still associated. Hence, we can only
|
|
|
- * get to lock contention here if userspace issues a command
|
|
|
- * that identified the hardware by wiphy index.
|
|
|
+ * a virtual interface is still present. Hence, we can only get
|
|
|
+ * to lock contention here if userspace issues a command that
|
|
|
+ * identified the hardware by wiphy index.
|
|
|
*/
|
|
|
cfg80211_lock_rdev(rdev);
|
|
|
-
|
|
|
- if (WARN_ON(rdev->scan_req)) {
|
|
|
- rdev->scan_req->aborted = true;
|
|
|
- ___cfg80211_scan_done(rdev);
|
|
|
- }
|
|
|
-
|
|
|
+ /* nothing */
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
|
|
|
- cfg80211_debugfs_rdev_del(rdev);
|
|
|
-
|
|
|
/* If this device got a regulatory hint tell core its
|
|
|
* free to listen now to a new shiny device regulatory hint */
|
|
|
reg_device_remove(wiphy);
|
|
|
|
|
|
- list_del(&rdev->list);
|
|
|
cfg80211_rdev_list_generation++;
|
|
|
device_del(&rdev->wiphy.dev);
|
|
|
debugfs_remove(rdev->wiphy.debugfsdir);
|
|
@@ -640,6 +650,31 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
|
|
|
}
|
|
|
EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
|
|
|
|
|
|
+static void wdev_cleanup_work(struct work_struct *work)
|
|
|
+{
|
|
|
+ struct wireless_dev *wdev;
|
|
|
+ struct cfg80211_registered_device *rdev;
|
|
|
+
|
|
|
+ wdev = container_of(work, struct wireless_dev, cleanup_work);
|
|
|
+ rdev = wiphy_to_dev(wdev->wiphy);
|
|
|
+
|
|
|
+ cfg80211_lock_rdev(rdev);
|
|
|
+
|
|
|
+ if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) {
|
|
|
+ rdev->scan_req->aborted = true;
|
|
|
+ ___cfg80211_scan_done(rdev);
|
|
|
+ }
|
|
|
+
|
|
|
+ cfg80211_unlock_rdev(rdev);
|
|
|
+
|
|
|
+ mutex_lock(&rdev->devlist_mtx);
|
|
|
+ rdev->opencount--;
|
|
|
+ mutex_unlock(&rdev->devlist_mtx);
|
|
|
+ wake_up(&rdev->dev_wait);
|
|
|
+
|
|
|
+ dev_put(wdev->netdev);
|
|
|
+}
|
|
|
+
|
|
|
static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
|
|
|
unsigned long state,
|
|
|
void *ndev)
|
|
@@ -663,6 +698,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
|
|
|
* are added with nl80211.
|
|
|
*/
|
|
|
mutex_init(&wdev->mtx);
|
|
|
+ INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
|
|
|
INIT_LIST_HEAD(&wdev->event_list);
|
|
|
spin_lock_init(&wdev->event_lock);
|
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
@@ -717,8 +753,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
|
|
|
default:
|
|
|
break;
|
|
|
}
|
|
|
+ dev_hold(dev);
|
|
|
+ schedule_work(&wdev->cleanup_work);
|
|
|
break;
|
|
|
case NETDEV_UP:
|
|
|
+ /*
|
|
|
+ * If we have a really quick DOWN/UP succession we may
|
|
|
+ * have this work still pending ... cancel it and see
|
|
|
+ * if it was pending, in which case we need to account
|
|
|
+ * for some of the work it would have done.
|
|
|
+ */
|
|
|
+ if (cancel_work_sync(&wdev->cleanup_work)) {
|
|
|
+ mutex_lock(&rdev->devlist_mtx);
|
|
|
+ rdev->opencount--;
|
|
|
+ mutex_unlock(&rdev->devlist_mtx);
|
|
|
+ dev_put(dev);
|
|
|
+ }
|
|
|
#ifdef CONFIG_WIRELESS_EXT
|
|
|
cfg80211_lock_rdev(rdev);
|
|
|
mutex_lock(&rdev->devlist_mtx);
|
|
@@ -734,6 +784,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
|
|
|
break;
|
|
|
}
|
|
|
wdev_unlock(wdev);
|
|
|
+ rdev->opencount++;
|
|
|
mutex_unlock(&rdev->devlist_mtx);
|
|
|
cfg80211_unlock_rdev(rdev);
|
|
|
#endif
|
|
@@ -756,7 +807,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
|
|
|
sysfs_remove_link(&dev->dev.kobj, "phy80211");
|
|
|
list_del_init(&wdev->list);
|
|
|
rdev->devlist_generation++;
|
|
|
- mutex_destroy(&wdev->mtx);
|
|
|
#ifdef CONFIG_WIRELESS_EXT
|
|
|
kfree(wdev->wext.keys);
|
|
|
#endif
|