Commit ad002395fd230528281083f4be71855ed7e35b04
Committed by
John W. Linville
1 parent
21f8a73f82
Exists in
master
and in
7 other branches
cfg80211: fix dangling scan request checking
My patch "cfg80211: fix deadlock" broke the code it was supposed to fix, the scan request checking. But it's not trivial to put it back the way it was, since the original patch had a deadlock. Now do it in a completely new way: queue the check off to a work struct, where we can freely lock. But that has some more complications, like needing to wait for it to be done before the wiphy/rdev can be destroyed, so some code is required to handle that. Signed-off-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Showing 3 changed files with 67 additions and 13 deletions Side-by-side Diff
include/net/cfg80211.h
net/wireless/core.c
... | ... | @@ -430,6 +430,8 @@ |
430 | 430 | INIT_WORK(&rdev->conn_work, cfg80211_conn_work); |
431 | 431 | INIT_WORK(&rdev->event_work, cfg80211_event_work); |
432 | 432 | |
433 | + init_waitqueue_head(&rdev->dev_wait); | |
434 | + | |
433 | 435 | /* |
434 | 436 | * Initialize wiphy parameters to IEEE 802.11 MIB default values. |
435 | 437 | * Fragmentation and RTS threshold are disabled by default with the |
436 | 438 | |
437 | 439 | |
438 | 440 | |
439 | 441 | |
440 | 442 | |
441 | 443 | |
... | ... | @@ -574,34 +576,42 @@ |
574 | 576 | /* protect the device list */ |
575 | 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 | 587 | BUG_ON(!list_empty(&rdev->netdev_list)); |
588 | + mutex_unlock(&rdev->devlist_mtx); | |
578 | 589 | |
579 | 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); | |
596 | + | |
597 | + /* | |
580 | 598 | * Try to grab rdev->mtx. If a command is still in progress, |
581 | 599 | * hopefully the driver will refuse it since it's tearing |
582 | 600 | * down the device already. We wait for this command to complete |
583 | 601 | * before unlinking the item from the list. |
584 | 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 | |
586 | - * get to lock contention here if userspace issues a command | |
587 | - * that identified the hardware by wiphy index. | |
603 | + * a virtual interface is still present. Hence, we can only get | |
604 | + * to lock contention here if userspace issues a command that | |
605 | + * identified the hardware by wiphy index. | |
588 | 606 | */ |
589 | 607 | cfg80211_lock_rdev(rdev); |
590 | - | |
591 | - if (WARN_ON(rdev->scan_req)) { | |
592 | - rdev->scan_req->aborted = true; | |
593 | - ___cfg80211_scan_done(rdev); | |
594 | - } | |
595 | - | |
608 | + /* nothing */ | |
596 | 609 | cfg80211_unlock_rdev(rdev); |
597 | 610 | |
598 | - cfg80211_debugfs_rdev_del(rdev); | |
599 | - | |
600 | 611 | /* If this device got a regulatory hint tell core its |
601 | 612 | * free to listen now to a new shiny device regulatory hint */ |
602 | 613 | reg_device_remove(wiphy); |
603 | 614 | |
604 | - list_del(&rdev->list); | |
605 | 615 | cfg80211_rdev_list_generation++; |
606 | 616 | device_del(&rdev->wiphy.dev); |
607 | 617 | debugfs_remove(rdev->wiphy.debugfsdir); |
... | ... | @@ -640,6 +650,31 @@ |
640 | 650 | } |
641 | 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 | 678 | static int cfg80211_netdev_notifier_call(struct notifier_block * nb, |
644 | 679 | unsigned long state, |
645 | 680 | void *ndev) |
... | ... | @@ -663,6 +698,7 @@ |
663 | 698 | * are added with nl80211. |
664 | 699 | */ |
665 | 700 | mutex_init(&wdev->mtx); |
701 | + INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | |
666 | 702 | INIT_LIST_HEAD(&wdev->event_list); |
667 | 703 | spin_lock_init(&wdev->event_lock); |
668 | 704 | mutex_lock(&rdev->devlist_mtx); |
669 | 705 | |
... | ... | @@ -717,8 +753,22 @@ |
717 | 753 | default: |
718 | 754 | break; |
719 | 755 | } |
756 | + dev_hold(dev); | |
757 | + schedule_work(&wdev->cleanup_work); | |
720 | 758 | break; |
721 | 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 | 772 | #ifdef CONFIG_WIRELESS_EXT |
723 | 773 | cfg80211_lock_rdev(rdev); |
724 | 774 | mutex_lock(&rdev->devlist_mtx); |
... | ... | @@ -734,6 +784,7 @@ |
734 | 784 | break; |
735 | 785 | } |
736 | 786 | wdev_unlock(wdev); |
787 | + rdev->opencount++; | |
737 | 788 | mutex_unlock(&rdev->devlist_mtx); |
738 | 789 | cfg80211_unlock_rdev(rdev); |
739 | 790 | #endif |
... | ... | @@ -756,7 +807,6 @@ |
756 | 807 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
757 | 808 | list_del_init(&wdev->list); |
758 | 809 | rdev->devlist_generation++; |
759 | - mutex_destroy(&wdev->mtx); | |
760 | 810 | #ifdef CONFIG_WIRELESS_EXT |
761 | 811 | kfree(wdev->wext.keys); |
762 | 812 | #endif |
net/wireless/core.h