summaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c97
1 files changed, 76 insertions, 21 deletions
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index b9074824862..df94b936526 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -21,6 +21,23 @@
#include "mesh.h"
#include "led.h"
+/**
+ * DOC: Interface list locking
+ *
+ * The interface list in each struct ieee80211_local is protected
+ * three-fold:
+ *
+ * (1) modifications may only be done under the RTNL
+ * (2) modifications and readers are protected against each other by
+ * the iflist_mtx.
+ * (3) modifications are done in an RCU manner so atomic readers
+ * can traverse the list in RCU-safe blocks.
+ *
+ * As a consequence, reads (traversals) of the list can be protected
+ * by either the RTNL, the iflist_mtx or RCU.
+ */
+
+
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
int meshhdrlen;
@@ -345,8 +362,7 @@ static int ieee80211_stop(struct net_device *dev)
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sta->sdata == sdata)
- ieee80211_sta_tear_down_BA_sessions(sdata,
- sta->sta.addr);
+ ieee80211_sta_tear_down_BA_sessions(sta);
}
rcu_read_unlock();
@@ -383,6 +399,8 @@ static int ieee80211_stop(struct net_device *dev)
atomic_dec(&local->iff_promiscs);
dev_mc_unsync(local->mdev, dev);
+ del_timer_sync(&local->dynamic_ps_timer);
+ cancel_work_sync(&local->dynamic_ps_enable_work);
/* APs need special treatment */
if (sdata->vif.type == NL80211_IFTYPE_AP) {
@@ -441,6 +459,7 @@ static int ieee80211_stop(struct net_device *dev)
WLAN_REASON_DEAUTH_LEAVING);
memset(sdata->u.sta.bssid, 0, ETH_ALEN);
+ del_timer_sync(&sdata->u.sta.chswitch_timer);
del_timer_sync(&sdata->u.sta.timer);
/*
* If the timer fired while we waited for it, it will have
@@ -450,6 +469,7 @@ static int ieee80211_stop(struct net_device *dev)
* it no longer is.
*/
cancel_work_sync(&sdata->u.sta.work);
+ cancel_work_sync(&sdata->u.sta.chswitch_work);
/*
* When we get here, the interface is marked down.
* Call synchronize_rcu() to wait for the RX path
@@ -459,7 +479,8 @@ static int ieee80211_stop(struct net_device *dev)
synchronize_rcu();
skb_queue_purge(&sdata->u.sta.skb_queue);
- sdata->u.sta.flags &= ~IEEE80211_STA_PRIVACY_INVOKED;
+ sdata->u.sta.flags &= ~(IEEE80211_STA_PRIVACY_INVOKED |
+ IEEE80211_STA_TKIP_WEP_USED);
kfree(sdata->u.sta.extra_ie);
sdata->u.sta.extra_ie = NULL;
sdata->u.sta.extra_ie_len = 0;
@@ -501,7 +522,7 @@ static int ieee80211_stop(struct net_device *dev)
* scan event to userspace -- the scan is incomplete.
*/
if (local->sw_scanning)
- ieee80211_scan_completed(&local->hw);
+ ieee80211_scan_completed(&local->hw, true);
}
conf.vif = &sdata->vif;
@@ -569,19 +590,6 @@ static void ieee80211_set_multicast_list(struct net_device *dev)
dev_mc_sync(local->mdev, dev);
}
-static void ieee80211_if_setup(struct net_device *dev)
-{
- ether_setup(dev);
- dev->hard_start_xmit = ieee80211_subif_start_xmit;
- dev->wireless_handlers = &ieee80211_iw_handler_def;
- dev->set_multicast_list = ieee80211_set_multicast_list;
- dev->change_mtu = ieee80211_change_mtu;
- dev->open = ieee80211_open;
- dev->stop = ieee80211_stop;
- dev->destructor = free_netdev;
- /* we will validate the address ourselves in ->open */
- dev->validate_addr = NULL;
-}
/*
* Called when the netdev is removed or, by the code below, before
* the interface type changes.
@@ -627,6 +635,13 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
kfree(sdata->u.sta.assocreq_ies);
kfree(sdata->u.sta.assocresp_ies);
kfree_skb(sdata->u.sta.probe_resp);
+ kfree(sdata->u.sta.ie_probereq);
+ kfree(sdata->u.sta.ie_proberesp);
+ kfree(sdata->u.sta.ie_auth);
+ kfree(sdata->u.sta.ie_assocreq);
+ kfree(sdata->u.sta.ie_reassocreq);
+ kfree(sdata->u.sta.ie_deauth);
+ kfree(sdata->u.sta.ie_disassoc);
break;
case NL80211_IFTYPE_WDS:
case NL80211_IFTYPE_AP_VLAN:
@@ -642,6 +657,34 @@ static void ieee80211_teardown_sdata(struct net_device *dev)
WARN_ON(flushed);
}
+static const struct net_device_ops ieee80211_dataif_ops = {
+ .ndo_open = ieee80211_open,
+ .ndo_stop = ieee80211_stop,
+ .ndo_uninit = ieee80211_teardown_sdata,
+ .ndo_start_xmit = ieee80211_subif_start_xmit,
+ .ndo_set_multicast_list = ieee80211_set_multicast_list,
+ .ndo_change_mtu = ieee80211_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static const struct net_device_ops ieee80211_monitorif_ops = {
+ .ndo_open = ieee80211_open,
+ .ndo_stop = ieee80211_stop,
+ .ndo_uninit = ieee80211_teardown_sdata,
+ .ndo_start_xmit = ieee80211_monitor_start_xmit,
+ .ndo_set_multicast_list = ieee80211_set_multicast_list,
+ .ndo_change_mtu = ieee80211_change_mtu,
+ .ndo_set_mac_address = eth_mac_addr,
+};
+
+static void ieee80211_if_setup(struct net_device *dev)
+{
+ ether_setup(dev);
+ dev->netdev_ops = &ieee80211_dataif_ops;
+ dev->wireless_handlers = &ieee80211_iw_handler_def;
+ dev->destructor = free_netdev;
+}
+
/*
* Helper function to initialise an interface to a specific type.
*/
@@ -653,7 +696,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
/* and set some type-dependent values */
sdata->vif.type = type;
- sdata->dev->hard_start_xmit = ieee80211_subif_start_xmit;
+ sdata->dev->netdev_ops = &ieee80211_dataif_ops;
sdata->wdev.iftype = type;
/* only monitor differs */
@@ -674,7 +717,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
break;
case NL80211_IFTYPE_MONITOR:
sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
- sdata->dev->hard_start_xmit = ieee80211_monitor_start_xmit;
+ sdata->dev->netdev_ops = &ieee80211_monitorif_ops;
sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
MONITOR_FLAG_OTHER_BSS;
break;
@@ -780,15 +823,15 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
if (ret)
goto fail;
- ndev->uninit = ieee80211_teardown_sdata;
-
if (ieee80211_vif_is_mesh(&sdata->vif) &&
params && params->mesh_id_len)
ieee80211_sdata_set_mesh_id(sdata,
params->mesh_id_len,
params->mesh_id);
+ mutex_lock(&local->iflist_mtx);
list_add_tail_rcu(&sdata->list, &local->interfaces);
+ mutex_unlock(&local->iflist_mtx);
if (new_dev)
*new_dev = ndev;
@@ -804,7 +847,10 @@ void ieee80211_if_remove(struct ieee80211_sub_if_data *sdata)
{
ASSERT_RTNL();
+ mutex_lock(&sdata->local->iflist_mtx);
list_del_rcu(&sdata->list);
+ mutex_unlock(&sdata->local->iflist_mtx);
+
synchronize_rcu();
unregister_netdevice(sdata->dev);
}
@@ -820,7 +866,16 @@ void ieee80211_remove_interfaces(struct ieee80211_local *local)
ASSERT_RTNL();
list_for_each_entry_safe(sdata, tmp, &local->interfaces, list) {
+ /*
+ * we cannot hold the iflist_mtx across unregister_netdevice,
+ * but we only need to hold it for list modifications to lock
+ * out readers since we're under the RTNL here as all other
+ * writers.
+ */
+ mutex_lock(&local->iflist_mtx);
list_del(&sdata->list);
+ mutex_unlock(&local->iflist_mtx);
+
unregister_netdevice(sdata->dev);
}
}