Commit d0709a65 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

mac80211: RCU-ify STA info structure access

This makes access to the STA hash table/list use RCU to protect
against freeing of items. However, it's not a true RCU, the
copy step is missing: whenever somebody changes a STA item it
is simply updated. This is an existing race condition that is
now somewhat understandable.

This patch also fixes the race key freeing vs. STA destruction
by making sure that sta_info_destroy() is always called under
RTNL and frees the key.
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5cf121c3
......@@ -471,10 +471,11 @@ static void rs_tx_status(void *priv_rate,
return;
}
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
return;
}
......@@ -547,7 +548,7 @@ static void rs_tx_status(void *priv_rate,
spin_unlock_irqrestore(&rs_sta->lock, flags);
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave\n");
......@@ -658,6 +659,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
IWL_DEBUG_RATE("enter\n");
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
/* Send management frames and broadcast/multicast data using lowest
......@@ -668,8 +671,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
!sta || !sta->rate_ctrl_priv) {
IWL_DEBUG_RATE("leave: No STA priv data to update!\n");
sel->rate = rate_lowest(local, band, sta);
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -811,7 +813,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
else
sta->txrate_idx = sta->last_txrate_idx;
sta_info_put(sta);
rcu_read_unlock();
IWL_DEBUG_RATE("leave: %d\n", index);
......@@ -843,13 +845,15 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
unsigned long now = jiffies;
u32 max_time = 0;
rcu_read_lock();
sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta) {
sta_info_put(sta);
if (sta)
IWL_DEBUG_RATE("leave - no private rate data!\n");
} else
else
IWL_DEBUG_RATE("leave - no station!\n");
rcu_read_unlock();
return sprintf(buf, "station %d not found\n", sta_id);
}
......@@ -890,7 +894,7 @@ int iwl3945_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
i = j;
}
spin_unlock_irqrestore(&rs_sta->lock, flags);
sta_info_put(sta);
rcu_read_unlock();
/* Display the average rate of all samples taken.
*
......@@ -927,11 +931,12 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
return;
}
rcu_read_lock();
sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
IWL_DEBUG_RATE("leave - no private rate data!\n");
rcu_read_unlock();
return;
}
......@@ -958,7 +963,7 @@ void iwl3945_rate_scale_init(struct ieee80211_hw *hw, s32 sta_id)
break;
}
sta_info_put(sta);
rcu_read_unlock();
spin_unlock_irqrestore(&rs_sta->lock, flags);
rssi = priv->last_rx_rssi;
......
......@@ -847,12 +847,12 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
if (retries > 15)
retries = 15;
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
if (!sta || !sta->rate_ctrl_priv) {
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -891,7 +891,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
if ((rs_index < 0) || (rs_index >= IWL_RATE_COUNT)) {
IWL_DEBUG_RATE("bad rate index at: %d rate 0x%X\n",
rs_index, tx_mcs.rate_n_flags);
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -909,7 +909,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
IWL_DEBUG_RATE("initial rate does not match 0x%x 0x%x\n",
tx_mcs.rate_n_flags,
le32_to_cpu(table->rs_table[0].rate_n_flags));
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -1025,7 +1025,7 @@ static void rs_tx_status(void *priv_rate, struct net_device *dev,
/* See if there's a better rate or modulation mode to try. */
rs_rate_scale_perform(priv, dev, hdr, sta);
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -2219,6 +2219,8 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
IWL_DEBUG_RATE_LIMIT("rate scale calculate new rate for skb\n");
rcu_read_lock();
sta = sta_info_get(local, hdr->addr1);
/* Send management frames and broadcast/multicast data using lowest
......@@ -2227,8 +2229,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
if (!ieee80211_is_data(fc) || is_multicast_ether_addr(hdr->addr1) ||
!sta || !sta->rate_ctrl_priv) {
sel->rate = rate_lowest(local, sband, sta);
if (sta)
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -2261,7 +2262,7 @@ static void rs_get_rate(void *priv_rate, struct net_device *dev,
sel->rate = rate_lowest(local, sband, sta);
return;
}
sta_info_put(sta);
rcu_read_unlock();
sel->rate = &priv->ieee_rates[i];
}
......@@ -2735,13 +2736,15 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
u32 max_time = 0;
u8 lq_type, antenna;
rcu_read_lock();
sta = sta_info_get(local, priv->stations[sta_id].sta.sta.addr);
if (!sta || !sta->rate_ctrl_priv) {
if (sta) {
sta_info_put(sta);
if (sta)
IWL_DEBUG_RATE("leave - no private rate data!\n");
} else
else
IWL_DEBUG_RATE("leave - no station!\n");
rcu_read_unlock();
return sprintf(buf, "station %d not found\n", sta_id);
}
......@@ -2808,7 +2811,7 @@ int iwl4965_fill_rs_info(struct ieee80211_hw *hw, char *buf, u8 sta_id)
"active_search %d rate index %d\n", lq_type, antenna,
lq_sta->search_better_tbl, sta->last_txrate_idx);
sta_info_put(sta);
rcu_read_unlock();
return cnt;
}
......
......@@ -136,7 +136,6 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta = NULL;
enum ieee80211_key_alg alg;
int ret;
struct ieee80211_key *key;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
......@@ -170,12 +169,7 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev,
ieee80211_key_link(key, sdata, sta);
ret = 0;
if (sta)
sta_info_put(sta);
return ret;
return 0;
}
static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
......@@ -184,7 +178,6 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
struct ieee80211_sub_if_data *sdata;
struct sta_info *sta;
int ret;
struct ieee80211_key *key;
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
......@@ -195,21 +188,18 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev,
ret = 0;
if (sta->key) {
key = sta->key;
ieee80211_key_free(key);
ieee80211_key_free(sta->key);
WARN_ON(sta->key);
} else
ret = -ENOENT;
sta_info_put(sta);
return ret;
}
if (!sdata->keys[key_idx])
return -ENOENT;
key = sdata->keys[key_idx];
ieee80211_key_free(key);
ieee80211_key_free(sdata->keys[key_idx]);
WARN_ON(sdata->keys[key_idx]);
return 0;
......@@ -292,8 +282,6 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev,
err = 0;
out:
if (sta)
sta_info_put(sta);
return err;
}
......@@ -311,7 +299,7 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
struct ieee80211_sub_if_data *sdata = sta->sdata;
sinfo->filled = STATION_INFO_INACTIVE_TIME |
STATION_INFO_RX_BYTES |
......@@ -340,16 +328,20 @@ static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
int ret = -ENOENT;
rcu_read_lock();
sta = sta_info_get_by_idx(local, idx, dev);
if (!sta)
return -ENOENT;
if (sta) {
ret = 0;
memcpy(mac, sta->addr, ETH_ALEN);
sta_set_sinfo(sta, sinfo);
}
memcpy(mac, sta->addr, ETH_ALEN);
sta_set_sinfo(sta, sinfo);
sta_info_put(sta);
rcu_read_unlock();
return 0;
return ret;
}
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
......@@ -357,16 +349,21 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct sta_info *sta;
int ret = -ENOENT;
sta = sta_info_get(local, mac);
if (!sta)
return -ENOENT;
rcu_read_lock();
/* XXX: verify sta->dev == dev */
sta_set_sinfo(sta, sinfo);
sta_info_put(sta);
return 0;
sta = sta_info_get(local, mac);
if (sta) {
ret = 0;
sta_set_sinfo(sta, sinfo);
}
rcu_read_unlock();
return ret;
}
/*
......@@ -559,8 +556,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta)
msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */
msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */
skb->dev = sta->dev;
skb->protocol = eth_type_trans(skb, sta->dev);
skb->dev = sta->sdata->dev;
skb->protocol = eth_type_trans(skb, sta->sdata->dev);
memset(skb->cb, 0, sizeof(skb->cb));
netif_rx(skb);
}
......@@ -572,7 +569,7 @@ static void sta_apply_parameters(struct ieee80211_local *local,
u32 rates;
int i, j;
struct ieee80211_supported_band *sband;
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
struct ieee80211_sub_if_data *sdata = sta->sdata;
if (params->station_flags & STATION_FLAG_CHANGED) {
sta->flags &= ~WLAN_STA_AUTHORIZED;
......@@ -644,14 +641,13 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
if (ieee80211_vif_is_mesh(&sdata->vif))
sta = mesh_plink_add(mac, DEFAULT_RATES, dev);
sta = mesh_plink_add(mac, DEFAULT_RATES, sdata);
else
sta = sta_info_add(local, dev, mac, GFP_KERNEL);
sta = sta_info_add(sdata, mac);
if (IS_ERR(sta))
return PTR_ERR(sta);
sta->dev = sdata->dev;
if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN ||
sdata->vif.type == IEEE80211_IF_TYPE_AP)
ieee80211_send_layer2_update(sta);
......@@ -662,15 +658,14 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
rate_control_rate_init(sta, local);
sta_info_put(sta);
return 0;
}
static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
u8 *mac)
{
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_local *local = sdata->local;
struct sta_info *sta;
if (mac) {
......@@ -679,10 +674,14 @@ static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev,
if (!sta)
return -ENOENT;
sta_info_free(sta);
sta_info_put(sta);
sta_info_unlink(&sta);
if (sta) {
synchronize_rcu();
sta_info_destroy(sta);
}
} else
sta_info_flush(local, dev);
sta_info_flush(local, sdata);
return 0;
}
......@@ -701,21 +700,19 @@ static int ieee80211_change_station(struct wiphy *wiphy,
if (!sta)
return -ENOENT;
if (params->vlan && params->vlan != sta->dev) {
if (params->vlan && params->vlan != sta->sdata->dev) {
vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN ||
vlansdata->vif.type != IEEE80211_IF_TYPE_AP)
return -EINVAL;
sta->dev = params->vlan;
sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan);
ieee80211_send_layer2_update(sta);
}
sta_apply_parameters(local, sta, params);
sta_info_put(sta);
return 0;
}
......@@ -735,23 +732,26 @@ static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
sta = sta_info_get(local, next_hop);
if (!sta)
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
err = mesh_path_add(dst, dev);
if (err)
if (err) {
rcu_read_unlock();
return err;
}
rcu_read_lock();
mpath = mesh_path_lookup(dst, dev);
if (!mpath) {
rcu_read_unlock();
sta_info_put(sta);
return -ENXIO;
}
mesh_path_fix_nexthop(mpath, sta);
sta_info_put(sta);
rcu_read_unlock();
return 0;
}
......@@ -760,7 +760,7 @@ static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
u8 *dst)
{
if (dst)
return mesh_path_del(dst, dev);
return mesh_path_del(dst, dev, false);
mesh_path_flush(dev);
return 0;
......@@ -781,20 +781,22 @@ static int ieee80211_change_mpath(struct wiphy *wiphy,
if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
return -ENOTSUPP;
rcu_read_lock();
sta = sta_info_get(local, next_hop);
if (!sta)
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
rcu_read_lock();
mpath = mesh_path_lookup(dst, dev);
if (!mpath) {
rcu_read_unlock();
sta_info_put(sta);
return -ENOENT;
}
mesh_path_fix_nexthop(mpath, sta);
sta_info_put(sta);
rcu_read_unlock();
return 0;
}
......
......@@ -51,7 +51,7 @@ static const struct file_operations sta_ ##name## _ops = { \
STA_OPS(name)
STA_FILE(aid, aid, D);
STA_FILE(dev, dev->name, S);
STA_FILE(dev, sdata->dev->name, S);
STA_FILE(rx_packets, rx_packets, LU);
STA_FILE(tx_packets, tx_packets, LU);
STA_FILE(rx_bytes, rx_bytes, LU);
......@@ -200,7 +200,7 @@ static ssize_t sta_agg_status_write(struct file *file,
const char __user *user_buf, size_t count, loff_t *ppos)
{
struct sta_info *sta = file->private_data;
struct net_device *dev = sta->dev;
struct net_device *dev = sta->sdata->dev;
struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
struct ieee80211_hw *hw = &local->hw;
u8 *da = sta->addr;
......
#ifndef __MAC80211_DEBUGFS_STA_H
#define __MAC80211_DEBUGFS_STA_H
#include "sta_info.h"
#ifdef CONFIG_MAC80211_DEBUGFS
void ieee80211_sta_debugfs_add(struct sta_info *sta);
void ieee80211_sta_debugfs_remove(struct sta_info *sta);
......
......@@ -375,15 +375,19 @@ static int ieee80211_stop(struct net_device *dev)
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
list_for_each_entry(sta, &local->sta_list, list) {
if (sta->dev == dev)
rcu_read_lock();
list_for_each_entry_rcu(sta, &local->sta_list, list) {
if (sta->sdata == sdata)
for (i = 0; i < STA_TID_NUM; i++)
ieee80211_sta_stop_rx_ba_session(sta->dev,
ieee80211_sta_stop_rx_ba_session(sdata->dev,
sta->addr, i,
WLAN_BACK_RECIPIENT,
WLAN_REASON_QSTA_LEAVE_QBSS);
}
rcu_read_unlock();
netif_stop_queue(dev);
/*
......@@ -449,7 +453,7 @@ static int ieee80211_stop(struct net_device *dev)
netif_tx_unlock_bh(local->mdev);
break;
case IEEE80211_IF_TYPE_MESH_POINT:
sta_info_flush(local, dev);
sta_info_flush(local, sdata);
/* fall through */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
......@@ -522,9 +526,12 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
printk(KERN_DEBUG "Could not find the station\n");
rcu_read_unlock();
return -ENOENT;
}
......@@ -564,7 +571,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
spin_unlock_bh(&local->mdev->queue_lock);
goto start_ba_exit;
}
sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
sdata = sta->sdata;
/* Ok, the Addba frame hasn't been sent yet, but if the driver calls the
* call back right away, it must see that the flow has begun */
......@@ -601,7 +608,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
sta->ampdu_mlme.dialog_token_allocator;
sta->ampdu_mlme.tid_tx[tid].ssn = start_seq_num;
ieee80211_send_addba_request(sta->dev, ra, tid,
ieee80211_send_addba_request(sta->sdata->dev, ra, tid,
sta->ampdu_mlme.tid_tx[tid].dialog_token,
sta->ampdu_mlme.tid_tx[tid].ssn,
0x40, 5000);
......@@ -614,7 +621,7 @@ int ieee80211_start_tx_ba_session(struct ieee80211_hw *hw, u8 *ra, u16 tid)
start_ba_exit:
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_session);
......@@ -637,9 +644,12 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
print_mac(mac, ra), tid);
#endif /* CONFIG_MAC80211_HT_DEBUG */
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta)
if (!sta) {
rcu_read_unlock();
return -ENOENT;
}
/* check if the TID is in aggregation */
state = &sta->ampdu_mlme.tid_tx[tid].state;
......@@ -673,7 +683,7 @@ int ieee80211_stop_tx_ba_session(struct ieee80211_hw *hw,
stop_BA_exit:
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
return ret;
}
EXPORT_SYMBOL(ieee80211_stop_tx_ba_session);
......@@ -691,8 +701,10 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
return;
}
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
rcu_read_unlock();
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
return;
......@@ -705,7 +717,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
printk(KERN_DEBUG "addBA was not requested yet, state is %d\n",
*state);
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
return;
}
......@@ -718,7 +730,7 @@ void ieee80211_start_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u16 tid)
ieee80211_wake_queue(hw, sta->tid_to_tx_q[tid]);
}
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
sta_info_put(sta);
rcu_read_unlock();
}
EXPORT_SYMBOL(ieee80211_start_tx_ba_cb);
......@@ -739,10 +751,12 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
printk(KERN_DEBUG "Stop a BA session requested on DA %s tid %d\n",
print_mac(mac, ra), tid);
rcu_read_lock();
sta = sta_info_get(local, ra);
if (!sta) {
printk(KERN_DEBUG "Could not find station: %s\n",
print_mac(mac, ra));
rcu_read_unlock();
return;
}
state = &sta->ampdu_mlme.tid_tx[tid].state;
......@@ -750,13 +764,13 @@ void ieee80211_stop_tx_ba_cb(struct ieee80211_hw *hw, u8 *ra, u8 tid)
spin_lock_bh(&sta->ampdu_mlme.ampdu_tx);
if ((*state & HT_AGG_STATE_REQ_STOP_BA_MSK) == 0) {
printk(KERN_DEBUG "unexpected callback to A-MPDU stop\n");
sta_info_put(sta);
spin_unlock_bh(&sta->ampdu_mlme.ampdu_tx);
rcu_read_unlock();
return;
}
if (*state & HT_AGG_STATE_INITIATOR_MSK)
ieee80211_send_delba(sta->dev, ra, tid,
ieee80211_send_delba(sta->sdata->dev, ra, tid,
WLAN_BACK_INITIATOR, WLAN_REASON_QSTA_NOT_USE);