diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/wireless/core.c | 76 | ||||
-rw-r--r-- | net/wireless/core.h | 2 |
2 files changed, 65 insertions, 13 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 69a185ba9ff1..c150071b6f29 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -430,6 +430,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
430 | INIT_WORK(&rdev->conn_work, cfg80211_conn_work); | 430 | INIT_WORK(&rdev->conn_work, cfg80211_conn_work); |
431 | INIT_WORK(&rdev->event_work, cfg80211_event_work); | 431 | INIT_WORK(&rdev->event_work, cfg80211_event_work); |
432 | 432 | ||
433 | init_waitqueue_head(&rdev->dev_wait); | ||
434 | |||
433 | /* | 435 | /* |
434 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. | 436 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. |
435 | * Fragmentation and RTS threshold are disabled by default with the | 437 | * Fragmentation and RTS threshold are disabled by default with the |
@@ -574,7 +576,23 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
574 | /* protect the device list */ | 576 | /* protect the device list */ |
575 | mutex_lock(&cfg80211_mutex); | 577 | mutex_lock(&cfg80211_mutex); |
576 | 578 | ||
579 | wait_event(rdev->dev_wait, ({ | ||
580 | int __count; | ||
581 | mutex_lock(&rdev->devlist_mtx); | ||
582 | __count = rdev->opencount; | ||
583 | mutex_unlock(&rdev->devlist_mtx); | ||
584 | __count == 0;})); | ||
585 | |||
586 | mutex_lock(&rdev->devlist_mtx); | ||
577 | BUG_ON(!list_empty(&rdev->netdev_list)); | 587 | BUG_ON(!list_empty(&rdev->netdev_list)); |
588 | mutex_unlock(&rdev->devlist_mtx); | ||
589 | |||
590 | /* | ||
591 | * First remove the hardware from everywhere, this makes | ||
592 | * it impossible to find from userspace. | ||
593 | */ | ||
594 | cfg80211_debugfs_rdev_del(rdev); | ||
595 | list_del(&rdev->list); | ||
578 | 596 | ||
579 | /* | 597 | /* |
580 | * Try to grab rdev->mtx. If a command is still in progress, | 598 | * Try to grab rdev->mtx. If a command is still in progress, |
@@ -582,26 +600,18 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
582 | * down the device already. We wait for this command to complete | 600 | * down the device already. We wait for this command to complete |
583 | * before unlinking the item from the list. | 601 | * before unlinking the item from the list. |
584 | * Note: as codified by the BUG_ON above we cannot get here if | 602 | * Note: as codified by the BUG_ON above we cannot get here if |
585 | * a virtual interface is still associated. Hence, we can only | 603 | * a virtual interface is still present. Hence, we can only get |
586 | * get to lock contention here if userspace issues a command | 604 | * to lock contention here if userspace issues a command that |
587 | * that identified the hardware by wiphy index. | 605 | * identified the hardware by wiphy index. |
588 | */ | 606 | */ |
589 | cfg80211_lock_rdev(rdev); | 607 | cfg80211_lock_rdev(rdev); |
590 | 608 | /* nothing */ | |
591 | if (WARN_ON(rdev->scan_req)) { | ||
592 | rdev->scan_req->aborted = true; | ||
593 | ___cfg80211_scan_done(rdev); | ||
594 | } | ||
595 | |||
596 | cfg80211_unlock_rdev(rdev); | 609 | cfg80211_unlock_rdev(rdev); |
597 | 610 | ||
598 | cfg80211_debugfs_rdev_del(rdev); | ||
599 | |||
600 | /* If this device got a regulatory hint tell core its | 611 | /* If this device got a regulatory hint tell core its |
601 | * free to listen now to a new shiny device regulatory hint */ | 612 | * free to listen now to a new shiny device regulatory hint */ |
602 | reg_device_remove(wiphy); | 613 | reg_device_remove(wiphy); |
603 | 614 | ||
604 | list_del(&rdev->list); | ||
605 | cfg80211_rdev_list_generation++; | 615 | cfg80211_rdev_list_generation++; |
606 | device_del(&rdev->wiphy.dev); | 616 | device_del(&rdev->wiphy.dev); |
607 | debugfs_remove(rdev->wiphy.debugfsdir); | 617 | debugfs_remove(rdev->wiphy.debugfsdir); |
@@ -640,6 +650,31 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked) | |||
640 | } | 650 | } |
641 | EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); | 651 | EXPORT_SYMBOL(wiphy_rfkill_set_hw_state); |
642 | 652 | ||
653 | static void wdev_cleanup_work(struct work_struct *work) | ||
654 | { | ||
655 | struct wireless_dev *wdev; | ||
656 | struct cfg80211_registered_device *rdev; | ||
657 | |||
658 | wdev = container_of(work, struct wireless_dev, cleanup_work); | ||
659 | rdev = wiphy_to_dev(wdev->wiphy); | ||
660 | |||
661 | cfg80211_lock_rdev(rdev); | ||
662 | |||
663 | if (WARN_ON(rdev->scan_req && rdev->scan_req->dev == wdev->netdev)) { | ||
664 | rdev->scan_req->aborted = true; | ||
665 | ___cfg80211_scan_done(rdev); | ||
666 | } | ||
667 | |||
668 | cfg80211_unlock_rdev(rdev); | ||
669 | |||
670 | mutex_lock(&rdev->devlist_mtx); | ||
671 | rdev->opencount--; | ||
672 | mutex_unlock(&rdev->devlist_mtx); | ||
673 | wake_up(&rdev->dev_wait); | ||
674 | |||
675 | dev_put(wdev->netdev); | ||
676 | } | ||
677 | |||
643 | static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | 678 | static int cfg80211_netdev_notifier_call(struct notifier_block * nb, |
644 | unsigned long state, | 679 | unsigned long state, |
645 | void *ndev) | 680 | void *ndev) |
@@ -663,6 +698,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
663 | * are added with nl80211. | 698 | * are added with nl80211. |
664 | */ | 699 | */ |
665 | mutex_init(&wdev->mtx); | 700 | mutex_init(&wdev->mtx); |
701 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | ||
666 | INIT_LIST_HEAD(&wdev->event_list); | 702 | INIT_LIST_HEAD(&wdev->event_list); |
667 | spin_lock_init(&wdev->event_lock); | 703 | spin_lock_init(&wdev->event_lock); |
668 | mutex_lock(&rdev->devlist_mtx); | 704 | mutex_lock(&rdev->devlist_mtx); |
@@ -717,8 +753,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
717 | default: | 753 | default: |
718 | break; | 754 | break; |
719 | } | 755 | } |
756 | dev_hold(dev); | ||
757 | schedule_work(&wdev->cleanup_work); | ||
720 | break; | 758 | break; |
721 | case NETDEV_UP: | 759 | case NETDEV_UP: |
760 | /* | ||
761 | * If we have a really quick DOWN/UP succession we may | ||
762 | * have this work still pending ... cancel it and see | ||
763 | * if it was pending, in which case we need to account | ||
764 | * for some of the work it would have done. | ||
765 | */ | ||
766 | if (cancel_work_sync(&wdev->cleanup_work)) { | ||
767 | mutex_lock(&rdev->devlist_mtx); | ||
768 | rdev->opencount--; | ||
769 | mutex_unlock(&rdev->devlist_mtx); | ||
770 | dev_put(dev); | ||
771 | } | ||
722 | #ifdef CONFIG_WIRELESS_EXT | 772 | #ifdef CONFIG_WIRELESS_EXT |
723 | cfg80211_lock_rdev(rdev); | 773 | cfg80211_lock_rdev(rdev); |
724 | mutex_lock(&rdev->devlist_mtx); | 774 | mutex_lock(&rdev->devlist_mtx); |
@@ -734,6 +784,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
734 | break; | 784 | break; |
735 | } | 785 | } |
736 | wdev_unlock(wdev); | 786 | wdev_unlock(wdev); |
787 | rdev->opencount++; | ||
737 | mutex_unlock(&rdev->devlist_mtx); | 788 | mutex_unlock(&rdev->devlist_mtx); |
738 | cfg80211_unlock_rdev(rdev); | 789 | cfg80211_unlock_rdev(rdev); |
739 | #endif | 790 | #endif |
@@ -756,7 +807,6 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
756 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 807 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
757 | list_del_init(&wdev->list); | 808 | list_del_init(&wdev->list); |
758 | rdev->devlist_generation++; | 809 | rdev->devlist_generation++; |
759 | mutex_destroy(&wdev->mtx); | ||
760 | #ifdef CONFIG_WIRELESS_EXT | 810 | #ifdef CONFIG_WIRELESS_EXT |
761 | kfree(wdev->wext.keys); | 811 | kfree(wdev->wext.keys); |
762 | #endif | 812 | #endif |
diff --git a/net/wireless/core.h b/net/wireless/core.h index c603f5286326..f565432ae22f 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -50,6 +50,8 @@ struct cfg80211_registered_device { | |||
50 | struct mutex devlist_mtx; | 50 | struct mutex devlist_mtx; |
51 | struct list_head netdev_list; | 51 | struct list_head netdev_list; |
52 | int devlist_generation; | 52 | int devlist_generation; |
53 | int opencount; /* also protected by devlist_mtx */ | ||
54 | wait_queue_head_t dev_wait; | ||
53 | 55 | ||
54 | /* BSSes/scanning */ | 56 | /* BSSes/scanning */ |
55 | spinlock_t bss_lock; | 57 | spinlock_t bss_lock; |