Merge branch 'master' into for-2.6.35
[safe/jmp/linux-2.6] / net / wireless / core.c
index 20db902..37d0e0a 100644 (file)
@@ -1,13 +1,14 @@
 /*
  * This is the linux wireless configuration interface.
  *
- * Copyright 2006-2009         Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2006-2010         Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include <linux/if.h>
 #include <linux/module.h>
 #include <linux/err.h>
 #include <linux/list.h>
+#include <linux/slab.h>
 #include <linux/nl80211.h>
 #include <linux/debugfs.h>
 #include <linux/notifier.h>
@@ -31,15 +32,10 @@ MODULE_AUTHOR("Johannes Berg");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("wireless configuration support");
 
-/* RCU might be appropriate here since we usually
- * only read the list, and that can happen quite
- * often because we need to do it for each command */
+/* RCU-protected (and cfg80211_mutex for writers) */
 LIST_HEAD(cfg80211_rdev_list);
 int cfg80211_rdev_list_generation;
 
-/*
- * This is used to protect the cfg80211_rdev_list
- */
 DEFINE_MUTEX(cfg80211_mutex);
 
 /* for debugfs */
@@ -418,6 +414,18 @@ int wiphy_register(struct wiphy *wiphy)
        int i;
        u16 ifmodes = wiphy->interface_modes;
 
+       if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
+               return -EINVAL;
+
+       if (WARN_ON(wiphy->addresses &&
+                   !is_zero_ether_addr(wiphy->perm_addr) &&
+                   memcmp(wiphy->perm_addr, wiphy->addresses[0].addr,
+                          ETH_ALEN)))
+               return -EINVAL;
+
+       if (wiphy->addresses)
+               memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
+
        /* sanity check ifmodes */
        WARN_ON(!ifmodes);
        ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
@@ -477,7 +485,7 @@ int wiphy_register(struct wiphy *wiphy)
        /* set up regulatory info */
        wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
 
-       list_add(&rdev->list, &cfg80211_rdev_list);
+       list_add_rcu(&rdev->list, &cfg80211_rdev_list);
        cfg80211_rdev_list_generation++;
 
        mutex_unlock(&cfg80211_mutex);
@@ -554,7 +562,8 @@ void wiphy_unregister(struct wiphy *wiphy)
         * it impossible to find from userspace.
         */
        debugfs_remove_recursive(rdev->wiphy.debugfsdir);
-       list_del(&rdev->list);
+       list_del_rcu(&rdev->list);
+       synchronize_rcu();
 
        /*
         * Try to grab rdev->mtx. If a command is still in progress,
@@ -669,8 +678,11 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                INIT_WORK(&wdev->cleanup_work, wdev_cleanup_work);
                INIT_LIST_HEAD(&wdev->event_list);
                spin_lock_init(&wdev->event_lock);
+               INIT_LIST_HEAD(&wdev->action_registrations);
+               spin_lock_init(&wdev->action_registrations_lock);
+
                mutex_lock(&rdev->devlist_mtx);
-               list_add(&wdev->list, &rdev->netdev_list);
+               list_add_rcu(&wdev->list, &rdev->netdev_list);
                rdev->devlist_generation++;
                /* can only change netns with wiphy */
                dev->features |= NETIF_F_NETNS_LOCAL;
@@ -687,19 +699,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                wdev->wext.default_key = -1;
                wdev->wext.default_mgmt_key = -1;
                wdev->wext.connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
+#endif
+
                if (wdev->wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT)
-                       wdev->wext.ps = true;
+                       wdev->ps = true;
                else
-                       wdev->wext.ps = false;
-               wdev->wext.ps_timeout = 100;
+                       wdev->ps = false;
+               /* allow mac80211 to determine the timeout */
+               wdev->ps_timeout = -1;
                if (rdev->ops->set_power_mgmt)
                        if (rdev->ops->set_power_mgmt(wdev->wiphy, dev,
-                                                     wdev->wext.ps,
-                                                     wdev->wext.ps_timeout)) {
+                                                     wdev->ps,
+                                                     wdev->ps_timeout)) {
                                /* assume this means it's off */
-                               wdev->wext.ps = false;
+                               wdev->ps = false;
                        }
-#endif
+
                if (!dev->ethtool_ops)
                        dev->ethtool_ops = &cfg80211_ethtool_ops;
 
@@ -782,13 +797,22 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
                 */
                if (!list_empty(&wdev->list)) {
                        sysfs_remove_link(&dev->dev.kobj, "phy80211");
-                       list_del_init(&wdev->list);
+                       list_del_rcu(&wdev->list);
                        rdev->devlist_generation++;
+                       cfg80211_mlme_purge_actions(wdev);
 #ifdef CONFIG_CFG80211_WEXT
                        kfree(wdev->wext.keys);
 #endif
                }
                mutex_unlock(&rdev->devlist_mtx);
+               /*
+                * synchronise (so that we won't find this netdev
+                * from other code any more) and then clear the list
+                * head so that the above code can safely check for
+                * !list_empty() to avoid double-cleanup.
+                */
+               synchronize_rcu();
+               INIT_LIST_HEAD(&wdev->list);
                break;
        case NETDEV_PRE_UP:
                if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))