diff options
Diffstat (limited to 'net/wireless/core.c')
| -rw-r--r-- | net/wireless/core.c | 59 |
1 files changed, 41 insertions, 18 deletions
diff --git a/net/wireless/core.c b/net/wireless/core.c index 92b812442488..7fdb9409ad2a 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | /* | 1 | /* |
| 2 | * This is the linux wireless configuration interface. | 2 | * This is the linux wireless configuration interface. |
| 3 | * | 3 | * |
| 4 | * Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net> | 4 | * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | #include <linux/if.h> | 7 | #include <linux/if.h> |
| @@ -31,15 +31,10 @@ MODULE_AUTHOR("Johannes Berg"); | |||
| 31 | MODULE_LICENSE("GPL"); | 31 | MODULE_LICENSE("GPL"); |
| 32 | MODULE_DESCRIPTION("wireless configuration support"); | 32 | MODULE_DESCRIPTION("wireless configuration support"); |
| 33 | 33 | ||
| 34 | /* RCU might be appropriate here since we usually | 34 | /* RCU-protected (and cfg80211_mutex for writers) */ |
| 35 | * only read the list, and that can happen quite | ||
| 36 | * often because we need to do it for each command */ | ||
| 37 | LIST_HEAD(cfg80211_rdev_list); | 35 | LIST_HEAD(cfg80211_rdev_list); |
| 38 | int cfg80211_rdev_list_generation; | 36 | int cfg80211_rdev_list_generation; |
| 39 | 37 | ||
| 40 | /* | ||
| 41 | * This is used to protect the cfg80211_rdev_list | ||
| 42 | */ | ||
| 43 | DEFINE_MUTEX(cfg80211_mutex); | 38 | DEFINE_MUTEX(cfg80211_mutex); |
| 44 | 39 | ||
| 45 | /* for debugfs */ | 40 | /* for debugfs */ |
| @@ -402,6 +397,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) | |||
| 402 | rdev->wiphy.retry_long = 4; | 397 | rdev->wiphy.retry_long = 4; |
| 403 | rdev->wiphy.frag_threshold = (u32) -1; | 398 | rdev->wiphy.frag_threshold = (u32) -1; |
| 404 | rdev->wiphy.rts_threshold = (u32) -1; | 399 | rdev->wiphy.rts_threshold = (u32) -1; |
| 400 | rdev->wiphy.coverage_class = 0; | ||
| 405 | 401 | ||
| 406 | return &rdev->wiphy; | 402 | return &rdev->wiphy; |
| 407 | } | 403 | } |
| @@ -417,6 +413,18 @@ int wiphy_register(struct wiphy *wiphy) | |||
| 417 | int i; | 413 | int i; |
| 418 | u16 ifmodes = wiphy->interface_modes; | 414 | u16 ifmodes = wiphy->interface_modes; |
| 419 | 415 | ||
| 416 | if (WARN_ON(wiphy->addresses && !wiphy->n_addresses)) | ||
| 417 | return -EINVAL; | ||
| 418 | |||
| 419 | if (WARN_ON(wiphy->addresses && | ||
| 420 | !is_zero_ether_addr(wiphy->perm_addr) && | ||
| 421 | memcmp(wiphy->perm_addr, wiphy->addresses[0].addr, | ||
| 422 | ETH_ALEN))) | ||
| 423 | return -EINVAL; | ||
| 424 | |||
| 425 | if (wiphy->addresses) | ||
| 426 | memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN); | ||
| 427 | |||
| 420 | /* sanity check ifmodes */ | 428 | /* sanity check ifmodes */ |
| 421 | WARN_ON(!ifmodes); | 429 | WARN_ON(!ifmodes); |
| 422 | ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; | 430 | ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1; |
| @@ -476,7 +484,7 @@ int wiphy_register(struct wiphy *wiphy) | |||
| 476 | /* set up regulatory info */ | 484 | /* set up regulatory info */ |
| 477 | wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); | 485 | wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE); |
| 478 | 486 | ||
| 479 | list_add(&rdev->list, &cfg80211_rdev_list); | 487 | list_add_rcu(&rdev->list, &cfg80211_rdev_list); |
| 480 | cfg80211_rdev_list_generation++; | 488 | cfg80211_rdev_list_generation++; |
| 481 | 489 | ||
| 482 | mutex_unlock(&cfg80211_mutex); | 490 | mutex_unlock(&cfg80211_mutex); |
| @@ -553,7 +561,8 @@ void wiphy_unregister(struct wiphy *wiphy) | |||
| 553 | * it impossible to find from userspace. | 561 | * it impossible to find from userspace. |
| 554 | */ | 562 | */ |
| 555 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); | 563 | debugfs_remove_recursive(rdev->wiphy.debugfsdir); |
| 556 | list_del(&rdev->list); | 564 | list_del_rcu(&rdev->list); |
| 565 | synchronize_rcu(); | ||
| 557 | 566 | ||
| 558 | /* | 567 | /* |
| 559 | * Try to grab rdev->mtx. If a command is still in progress, | 568 | * Try to grab rdev->mtx. If a command is still in progress, |
| @@ -668,8 +677,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
| 668 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); | 677 | INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work); |
| 669 | INIT_LIST_HEAD(&wdev->event_list); | 678 | INIT_LIST_HEAD(&wdev->event_list); |
| 670 | spin_lock_init(&wdev->event_lock); | 679 | spin_lock_init(&wdev->event_lock); |
| 680 | INIT_LIST_HEAD(&wdev->action_registrations); | ||
| 681 | spin_lock_init(&wdev->action_registrations_lock); | ||
| 682 | |||
| 671 | mutex_lock(&rdev->devlist_mtx); | 683 | mutex_lock(&rdev->devlist_mtx); |
| 672 | list_add(&wdev->list, &rdev->netdev_list); | 684 | list_add_rcu(&wdev->list, &rdev->netdev_list); |
| 673 | rdev->devlist_generation++; | 685 | rdev->devlist_generation++; |
| 674 | /* can only change netns with wiphy */ | 686 | /* can only change netns with wiphy */ |
| 675 | dev->features |= NETIF_F_NETNS_LOCAL; | 687 | dev->features |= NETIF_F_NETNS_LOCAL; |
| @@ -686,19 +698,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
| 686 | wdev->wext.default_key = -1; | 698 | wdev->wext.default_key = -1; |
| 687 | wdev->wext.default_mgmt_key = -1; | 699 | wdev->wext.default_mgmt_key = -1; |
| 688 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | 700 | wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; |
| 701 | #endif | ||
| 702 | |||
| 689 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) | 703 | if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT) |
| 690 | wdev->wext.ps = true; | 704 | wdev->ps = true; |
| 691 | else | 705 | else |
| 692 | wdev->wext.ps = false; | 706 | wdev->ps = false; |
| 693 | wdev->wext.ps_timeout = 100; | 707 | wdev->ps_timeout = 100; |
| 694 | if (rdev->ops->set_power_mgmt) | 708 | if (rdev->ops->set_power_mgmt) |
| 695 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, | 709 | if (rdev->ops->set_power_mgmt(wdev->wiphy, dev, |
| 696 | wdev->wext.ps, | 710 | wdev->ps, |
| 697 | wdev->wext.ps_timeout)) { | 711 | wdev->ps_timeout)) { |
| 698 | /* assume this means it's off */ | 712 | /* assume this means it's off */ |
| 699 | wdev->wext.ps = false; | 713 | wdev->ps = false; |
| 700 | } | 714 | } |
| 701 | #endif | 715 | |
| 702 | if (!dev->ethtool_ops) | 716 | if (!dev->ethtool_ops) |
| 703 | dev->ethtool_ops = &cfg80211_ethtool_ops; | 717 | dev->ethtool_ops = &cfg80211_ethtool_ops; |
| 704 | 718 | ||
| @@ -781,13 +795,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
| 781 | */ | 795 | */ |
| 782 | if (!list_empty(&wdev->list)) { | 796 | if (!list_empty(&wdev->list)) { |
| 783 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); | 797 | sysfs_remove_link(&dev->dev.kobj, "phy80211"); |
| 784 | list_del_init(&wdev->list); | 798 | list_del_rcu(&wdev->list); |
| 785 | rdev->devlist_generation++; | 799 | rdev->devlist_generation++; |
| 800 | cfg80211_mlme_purge_actions(wdev); | ||
| 786 | #ifdef CONFIG_CFG80211_WEXT | 801 | #ifdef CONFIG_CFG80211_WEXT |
| 787 | kfree(wdev->wext.keys); | 802 | kfree(wdev->wext.keys); |
| 788 | #endif | 803 | #endif |
| 789 | } | 804 | } |
| 790 | mutex_unlock(&rdev->devlist_mtx); | 805 | mutex_unlock(&rdev->devlist_mtx); |
| 806 | /* | ||
| 807 | * synchronise (so that we won't find this netdev | ||
| 808 | * from other code any more) and then clear the list | ||
| 809 | * head so that the above code can safely check for | ||
| 810 | * !list_empty() to avoid double-cleanup. | ||
| 811 | */ | ||
| 812 | synchronize_rcu(); | ||
| 813 | INIT_LIST_HEAD(&wdev->list); | ||
| 791 | break; | 814 | break; |
| 792 | case NETDEV_PRE_UP: | 815 | case NETDEV_PRE_UP: |
| 793 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) | 816 | if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype))) |
