summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJohannes Berg <johannes@sipsolutions.net>2008-02-25 16:27:46 +0100
committerJohn W. Linville <linville@tuxdriver.com>2008-03-06 15:30:46 -0500
commitd0709a65181beb787ef3f58cfe45536a2bb254c8 (patch)
tree29e5f36583b0e0a3f11b291347e57672eab41dad /drivers
parent5cf121c3cdb955583bf0c5d28c992b7968a4aa1a (diff)
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: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945-rs.c31
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-4965-rs.c27
2 files changed, 33 insertions, 25 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
index a8223c4cc97..c4bfba6f3c2 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945-rs.c
@@ -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;
diff --git a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
index 48a6a85355e..46d85fd07fa 100644
--- a/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
+++ b/drivers/net/wireless/iwlwifi/iwl-4965-rs.c
@@ -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;
}